Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): Add webhooks for network tokenization #6695

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion crates/api_models/src/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ pub enum WebhookResponseTracker {
mandate_id: String,
status: common_enums::MandateStatus,
},
PaymentMethod {
payment_method_id: String,
status: common_enums::PaymentMethodStatus,
},
NoEffect,
}

Expand All @@ -129,7 +133,22 @@ impl WebhookResponseTracker {
Self::Payment { payment_id, .. }
| Self::Refund { payment_id, .. }
| Self::Dispute { payment_id, .. } => Some(payment_id.to_owned()),
Self::NoEffect | Self::Mandate { .. } => None,
Self::NoEffect | Self::Mandate { .. } | Self::PaymentMethod { .. } => None,
#[cfg(feature = "payouts")]
Self::Payout { .. } => None,
}
}

pub fn get_payment_method_id(&self) -> Option<String> {
match self {
Self::PaymentMethod {
payment_method_id, ..
} => Some(payment_method_id.to_owned()),
Self::Payment { .. }
| Self::Refund { .. }
| Self::Dispute { .. }
| Self::NoEffect
| Self::Mandate { .. } => None,
#[cfg(feature = "payouts")]
Self::Payout { .. } => None,
}
Expand Down
3 changes: 3 additions & 0 deletions crates/common_utils/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub enum ApiEventsType {
connector: String,
payment_id: Option<id_type::PaymentId>,
},
NetworkTokenWebhook {
payment_method_id: Option<String>,
},
#[cfg(feature = "v2")]
Webhooks {
connector: id_type::MerchantConnectorAccountId,
Expand Down
27 changes: 27 additions & 0 deletions crates/diesel_models/src/callback_mapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use common_utils::pii;
use diesel::{Identifiable, Insertable, Queryable, Selectable};
use serde::{self, Deserialize, Serialize};

use crate::schema::callback_mapper;

#[derive(
Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize,
)]
#[diesel(table_name = callback_mapper, primary_key(id), check_for_backend(diesel::pg::Pg))]
pub struct CallBackMapper {
pub id: String,
#[serde(rename = "type")]
pub type_: String,
pub data: pii::SecretSerdeValue,
pub created_at: time::PrimitiveDateTime,
pub last_modified_at: time::PrimitiveDateTime,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Insertable)]
#[diesel(table_name = callback_mapper)]
pub struct CallBackMapperNew {
pub id: String,
#[serde(rename = "type")]
pub type_: String,
pub data: pii::SecretSerdeValue,
}
11 changes: 6 additions & 5 deletions crates/diesel_models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod authentication;
pub mod authorization;
pub mod blocklist;
pub mod blocklist_fingerprint;
pub mod callback_mapper;
pub mod customers;
pub mod dispute;
pub mod enums;
Expand Down Expand Up @@ -59,11 +60,11 @@ use diesel_impl::{DieselArray, OptionalDieselArray};
pub type StorageResult<T> = error_stack::Result<T, errors::DatabaseError>;
pub type PgPooledConn = async_bb8_diesel::Connection<diesel::PgConnection>;
pub use self::{
address::*, api_keys::*, cards_info::*, configs::*, customers::*, dispute::*, ephemeral_key::*,
events::*, file::*, generic_link::*, locker_mock_up::*, mandate::*, merchant_account::*,
merchant_connector_account::*, payment_attempt::*, payment_intent::*, payment_method::*,
payout_attempt::*, payouts::*, process_tracker::*, refund::*, reverse_lookup::*,
user_authentication_method::*,
address::*, api_keys::*, callback_mapper::*, cards_info::*, configs::*, customers::*,
dispute::*, ephemeral_key::*, events::*, file::*, generic_link::*, locker_mock_up::*,
mandate::*, merchant_account::*, merchant_connector_account::*, payment_attempt::*,
payment_intent::*, payment_method::*, payout_attempt::*, payouts::*, process_tracker::*,
refund::*, reverse_lookup::*, user_authentication_method::*,
};

/// The types and implementations provided by this module are required for the schema generated by
Expand Down
1 change: 1 addition & 0 deletions crates/diesel_models/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod authentication;
pub mod authorization;
pub mod blocklist;
pub mod blocklist_fingerprint;
pub mod callback_mapper;
pub mod customers;
pub mod dashboard_metadata;
pub mod dispute;
Expand Down
24 changes: 24 additions & 0 deletions crates/diesel_models/src/query/callback_mapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use diesel::{associations::HasTable, ExpressionMethods};

use super::generics;
use crate::{
callback_mapper::{CallBackMapper, CallBackMapperNew},
schema::callback_mapper::dsl,
PgPooledConn, StorageResult,
};

impl CallBackMapperNew {
pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<CallBackMapper> {
generics::generic_insert(conn, self).await
}
}

impl CallBackMapper {
pub async fn find_by_id(conn: &PgPooledConn, id: &str) -> StorageResult<Self> {
generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
conn,
dsl::id.eq(id.to_owned()),
)
.await
}
}
17 changes: 17 additions & 0 deletions crates/diesel_models/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,22 @@ diesel::table! {
}
}

diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;

callback_mapper (id) {
#[max_length = 128]
id -> Varchar,
#[sql_name = "type"]
#[max_length = 64]
type_ -> Varchar,
data -> Jsonb,
created_at -> Timestamp,
last_modified_at -> Timestamp,
}
}

diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;
Expand Down Expand Up @@ -1404,6 +1420,7 @@ diesel::allow_tables_to_appear_in_same_query!(
blocklist_fingerprint,
blocklist_lookup,
business_profile,
callback_mapper,
captures,
cards_info,
configs,
Expand Down
17 changes: 17 additions & 0 deletions crates/diesel_models/src/schema_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,22 @@ diesel::table! {
}
}

diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;

callback_mapper (id) {
#[max_length = 128]
id -> Varchar,
#[sql_name = "type"]
#[max_length = 64]
type_ -> Varchar,
data -> Jsonb,
created_at -> Timestamp,
last_modified_at -> Timestamp,
}
}

diesel::table! {
use diesel::sql_types::*;
use crate::enums::diesel_exports::*;
Expand Down Expand Up @@ -1352,6 +1368,7 @@ diesel::allow_tables_to_appear_in_same_query!(
blocklist_fingerprint,
blocklist_lookup,
business_profile,
callback_mapper,
captures,
cards_info,
configs,
Expand Down
30 changes: 30 additions & 0 deletions crates/hyperswitch_domain_models/src/callback_mapper.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use common_utils::{id_type, pii};
use serde::{self, Deserialize, Serialize};

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct CallBackMapper {
pub id: String,
#[serde(rename = "type")]
pub type_: String,
pub data: pii::SecretSerdeValue,
pub created_at: time::PrimitiveDateTime,
pub last_modified_at: time::PrimitiveDateTime,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct CallBackMapperNew {
pub id: String,
#[serde(rename = "type")]
pub type_: String,
pub data: pii::SecretSerdeValue,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[serde(tag = "type", content = "data")]
pub enum CallBackMapperData {
NetworkTokenWebhook {
merchant_id: id_type::MerchantId,
payment_method_id: String,
customer_id: id_type::CustomerId,
},
}
1 change: 1 addition & 0 deletions crates/hyperswitch_domain_models/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod api;
pub mod behaviour;
pub mod business_profile;
pub mod callback_mapper;
pub mod consts;
pub mod customer;
pub mod disputes;
Expand Down
4 changes: 4 additions & 0 deletions crates/router/src/configs/secrets_transformers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,15 @@ impl SecretsHandler for settings::NetworkTokenizationService {
let private_key = secret_management_client
.get_secret(network_tokenization.private_key.clone())
.await?;
let webhook_source_verification_key = secret_management_client
.get_secret(network_tokenization.webhook_source_verification_key.clone())
.await?;

Ok(value.transition_state(|network_tokenization| Self {
public_key,
private_key,
token_service_api_key,
webhook_source_verification_key,
..network_tokenization
}))
}
Expand Down
1 change: 1 addition & 0 deletions crates/router/src/configs/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ pub struct NetworkTokenizationService {
pub key_id: String,
pub delete_token_url: url::Url,
pub check_token_status_url: url::Url,
pub webhook_source_verification_key: Secret<String>,
}

#[derive(Debug, Deserialize, Clone)]
Expand Down
11 changes: 10 additions & 1 deletion crates/router/src/configs/validations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,16 @@ impl super::settings::NetworkTokenizationService {
Err(ApplicationError::InvalidConfigurationValueError(
"private_key must not be empty".into(),
))
})
})?;

when(
self.webhook_source_verification_key.is_default_or_empty(),
|| {
Err(ApplicationError::InvalidConfigurationValueError(
"webhook_source_verification_key must not be empty".into(),
))
},
)
}
}

Expand Down
Loading
Loading