feat(payments_v2): add payment_confirm_intent api endpoint (#6263)

Co-authored-by: hrithikesh026 <hrithikesh.vm@juspay.in>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: sai-harsha-vardhan <harsha111hero@gmail.com>
Co-authored-by: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com>
Co-authored-by: spritianeja03 <146620839+spritianeja03@users.noreply.github.com>
Co-authored-by: Spriti Aneja <spriti.aneja@juspay.in>
This commit is contained in:
Narayan Bhat
2024-10-24 16:10:01 +05:30
committed by GitHub
parent 26e0c32f4d
commit c7c1e1adab
125 changed files with 3930 additions and 3481 deletions

View File

@ -105,6 +105,15 @@ impl ApiEventMetric for id_type::PaymentId {
}
}
#[cfg(feature = "v2")]
impl ApiEventMetric for id_type::GlobalPaymentId {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
Some(ApiEventsType::Payment {
payment_id: self.clone(),
})
}
}
impl<Q: ApiEventMetric, E> ApiEventMetric for Result<Q, E> {
fn get_api_event_type(&self) -> Option<ApiEventsType> {
match self {

View File

@ -27,7 +27,9 @@ use diesel::{
};
#[cfg(feature = "v2")]
pub use global_id::{
payment::GlobalPaymentId, payment_methods::GlobalPaymentMethodId, refunds::GlobalRefundId,
payment::{GlobalAttemptId, GlobalPaymentId},
payment_methods::GlobalPaymentMethodId,
refunds::GlobalRefundId,
CellId,
};
pub use merchant::MerchantId;

View File

@ -1,4 +1,3 @@
#![allow(unused)]
pub mod payment;
pub mod payment_methods;
pub mod refunds;
@ -24,6 +23,7 @@ pub(crate) struct GlobalId(LengthId<MAX_GLOBAL_ID_LENGTH, MIN_GLOBAL_ID_LENGTH>)
pub(crate) enum GlobalEntity {
Customer,
Payment,
Attempt,
PaymentMethod,
Refund,
}
@ -34,6 +34,7 @@ impl GlobalEntity {
Self::Customer => "cus",
Self::Payment => "pay",
Self::PaymentMethod => "pm",
Self::Attempt => "att",
Self::Refund => "ref",
}
}

View File

@ -2,22 +2,16 @@ use error_stack::ResultExt;
use crate::{errors, generate_id_with_default_len, generate_time_ordered_id_without_prefix, types};
/// A global id that can be used to identify a payment
#[derive(
Debug,
Clone,
Hash,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
diesel::expression::AsExpression,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
pub struct GlobalPaymentId(super::GlobalId);
crate::global_id_type!(
GlobalPaymentId,
"A global id that can be used to identify a payment
The format will be `<cell_id>_<entity_prefix>_<time_ordered_id>`
example - cell1_pay_uu1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p"
);
// Database related implementations so that this field can be used directly in the database tables
crate::impl_queryable_id_type!(GlobalPaymentId);
crate::impl_to_sql_from_sql_global_id_type!(GlobalPaymentId);
impl GlobalPaymentId {
/// Get string representation of the id
@ -51,26 +45,24 @@ impl TryFrom<std::borrow::Cow<'static, str>> for GlobalPaymentId {
}
}
// TODO: refactor the macro to include this id use case as well
impl<DB> diesel::serialize::ToSql<diesel::sql_types::Text, DB> for GlobalPaymentId
where
DB: diesel::backend::Backend,
super::GlobalId: diesel::serialize::ToSql<diesel::sql_types::Text, DB>,
{
fn to_sql<'b>(
&'b self,
out: &mut diesel::serialize::Output<'b, '_, DB>,
) -> diesel::serialize::Result {
self.0.to_sql(out)
}
}
crate::global_id_type!(
GlobalAttemptId,
"A global id that can be used to identify a payment attempt"
);
impl<DB> diesel::deserialize::FromSql<diesel::sql_types::Text, DB> for GlobalPaymentId
where
DB: diesel::backend::Backend,
super::GlobalId: diesel::deserialize::FromSql<diesel::sql_types::Text, DB>,
{
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
super::GlobalId::from_sql(value).map(Self)
// Database related implementations so that this field can be used directly in the database tables
crate::impl_queryable_id_type!(GlobalAttemptId);
crate::impl_to_sql_from_sql_global_id_type!(GlobalAttemptId);
impl GlobalAttemptId {
/// Generate a new GlobalAttemptId from a cell id
pub fn generate(cell_id: &super::CellId) -> Self {
let global_id = super::GlobalId::generate(cell_id.clone(), super::GlobalEntity::Attempt);
Self(global_id)
}
/// Get string representation of the id
pub fn get_string_repr(&self) -> &str {
self.0.get_string_repr()
}
}

View File

@ -172,6 +172,27 @@ mod id_type {
};
}
/// Defines a Global Id type
#[cfg(feature = "v2")]
#[macro_export]
macro_rules! global_id_type {
($type:ident, $doc:literal) => {
#[doc = $doc]
#[derive(
Debug,
Clone,
Hash,
PartialEq,
Eq,
serde::Serialize,
serde::Deserialize,
diesel::expression::AsExpression,
)]
#[diesel(sql_type = diesel::sql_types::Text)]
pub struct $type($crate::id_type::global_id::GlobalId);
};
}
/// Implements common methods on the specified ID type.
#[macro_export]
macro_rules! impl_id_type_methods {
@ -292,6 +313,40 @@ mod id_type {
};
}
#[cfg(feature = "v2")]
/// Implements the `ToSql` and `FromSql` traits on the specified Global ID type.
#[macro_export]
macro_rules! impl_to_sql_from_sql_global_id_type {
($type:ty, $diesel_type:ty) => {
impl<DB> diesel::serialize::ToSql<$diesel_type, DB> for $type
where
DB: diesel::backend::Backend,
$crate::id_type::global_id::GlobalId: diesel::serialize::ToSql<$diesel_type, DB>,
{
fn to_sql<'b>(
&'b self,
out: &mut diesel::serialize::Output<'b, '_, DB>,
) -> diesel::serialize::Result {
self.0.to_sql(out)
}
}
impl<DB> diesel::deserialize::FromSql<$diesel_type, DB> for $type
where
DB: diesel::backend::Backend,
$crate::id_type::global_id::GlobalId:
diesel::deserialize::FromSql<$diesel_type, DB>,
{
fn from_sql(value: DB::RawValue<'_>) -> diesel::deserialize::Result<Self> {
$crate::id_type::global_id::GlobalId::from_sql(value).map(Self)
}
}
};
($type:ty) => {
$crate::impl_to_sql_from_sql_global_id_type!($type, diesel::sql_types::Text);
};
}
/// Implements the `Queryable` trait on the specified ID type.
#[macro_export]
macro_rules! impl_queryable_id_type {

View File

@ -603,6 +603,13 @@ impl StringMajorUnit {
/// This domain type can be used for any url
pub struct Url(url::Url);
impl Url {
/// Get string representation of the url
pub fn get_string_repr(&self) -> &str {
self.0.as_str()
}
}
impl<DB> ToSql<sql_types::Text, DB> for Url
where
DB: Backend,
@ -667,6 +674,30 @@ mod client_secret_type {
}
}
impl FromStr for ClientSecret {
type Err = ParsingError;
fn from_str(str_value: &str) -> Result<Self, Self::Err> {
let (payment_id, secret) =
str_value
.rsplit_once("_secret_")
.ok_or(ParsingError::EncodeError(
"Expected a string in the format '{payment_id}_secret_{secret}'",
))?;
let payment_id = id_type::GlobalPaymentId::try_from(Cow::Owned(payment_id.to_owned()))
.map_err(|err| {
logger::error!(global_payment_id_error=?err);
ParsingError::EncodeError("Error while constructing GlobalPaymentId")
})?;
Ok(Self {
payment_id,
secret: masking::Secret::new(secret.to_owned()),
})
}
}
impl<'de> Deserialize<'de> for ClientSecret {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
@ -1096,6 +1127,12 @@ impl Description {
pub fn from_str_unchecked(input_str: &'static str) -> Self {
Self(LengthString::new_unchecked(input_str.to_owned()))
}
// TODO: Remove this function in future once description in router data is updated to domain type
/// Get the string representation of the description
pub fn get_string_repr(&self) -> &str {
&self.0 .0
}
}
/// Domain type for Statement Descriptor
@ -1273,6 +1310,58 @@ where
}
}
#[cfg(feature = "v2")]
/// Browser information to be used for 3DS 2.0
// If any of the field is PII, then we can make them as secret
#[derive(
ToSchema,
Debug,
Clone,
serde::Deserialize,
serde::Serialize,
Eq,
PartialEq,
diesel::AsExpression,
)]
#[diesel(sql_type = Jsonb)]
pub struct BrowserInformation {
/// Color depth supported by the browser
pub color_depth: Option<u8>,
/// Whether java is enabled in the browser
pub java_enabled: Option<bool>,
/// Whether javascript is enabled in the browser
pub java_script_enabled: Option<bool>,
/// Language supported
pub language: Option<String>,
/// The screen height in pixels
pub screen_height: Option<u32>,
/// The screen width in pixels
pub screen_width: Option<u32>,
/// Time zone of the client
pub time_zone: Option<i32>,
/// Ip address of the client
#[schema(value_type = Option<String>)]
pub ip_address: Option<std::net::IpAddr>,
/// List of headers that are accepted
#[schema(
example = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
)]
pub accept_header: Option<String>,
/// User-agent of the browser
pub user_agent: Option<String>,
}
#[cfg(feature = "v2")]
crate::impl_to_sql_from_sql_json!(BrowserInformation);
/// Domain type for connector_transaction_id
/// Maximum length for connector's transaction_id can be 128 characters in HS DB.
/// In case connector's use an identifier whose length exceeds 128 characters,