Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
57b6b86
add template code for chargebee
Jan 15, 2025
d27700e
merge main
Jan 15, 2025
b3908c0
chore: run formatter
hyperswitch-bot[bot] Jan 15, 2025
ff41480
add recovery incoming webhooks support
Jan 19, 2025
d5d14fe
add feature metadata queries
Jan 21, 2025
dcc6b1f
clippy fix
Jan 21, 2025
55f3018
Merge branch 'main' into chargebee_integration
srujanchikke Jan 21, 2025
7f05e1a
merge main
Jan 26, 2025
251f492
chore: run formatter
hyperswitch-bot[bot] Jan 26, 2025
9619365
Merge branch 'chargebee_integration' into recovery_webhooks
srujanchikke Jan 27, 2025
6866eaf
chore: run formatter
hyperswitch-bot[bot] Jan 27, 2025
7994d71
Merge branch 'main' into recovery_webhooks
srujanchikke Jan 29, 2025
b0699a3
Merge branch 'main' into recovery_webhooks
Feb 10, 2025
f92ee73
Merge branch 'main' into recovery_webhooks
Feb 12, 2025
ef12ef3
Merge branch 'main' into recovery_webhooks
Feb 13, 2025
aff2350
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 14, 2025
4203413
payment attempt feature metadata refactor
Feb 16, 2025
1c60b72
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 16, 2025
6e5cd99
chore: run formatter
hyperswitch-bot[bot] Feb 16, 2025
a75741a
clippy fix
Feb 17, 2025
d22da80
chore: run formatter
hyperswitch-bot[bot] Feb 17, 2025
9b3cd47
minor refactor
Feb 17, 2025
13d6e91
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 17, 2025
f63cb5e
change feature flag name
Feb 17, 2025
e22e24b
chore: run formatter
hyperswitch-bot[bot] Feb 17, 2025
e8c46e2
clippy fix v2
Feb 18, 2025
a753edc
typos fix
Feb 18, 2025
a675dc8
add openapi spec changes
Feb 18, 2025
1a3b3ed
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 18, 2025
bde7819
add recovery webhooks
Feb 18, 2025
3413b69
feature flag revert
Feb 18, 2025
b18f5d7
spell check fix
Feb 18, 2025
2affe4a
Merge branch 'main' into recovery_webhooks
Feb 18, 2025
61aee66
resolve comments
Feb 19, 2025
1e8eb88
clippy fix
Feb 19, 2025
6cc5d2d
resolve comments
Feb 19, 2025
51efe70
clippy fix
Feb 19, 2025
be03d4c
chore: run formatter
hyperswitch-bot[bot] Feb 19, 2025
5353ae6
resolve comments
Feb 20, 2025
f333fa0
revert toml
Feb 20, 2025
488563f
chore: run formatter
hyperswitch-bot[bot] Feb 20, 2025
675c5c5
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 20, 2025
ced7309
open api fix
Feb 20, 2025
03f4ad4
docs(openapi): re-generate OpenAPI specification
hyperswitch-bot[bot] Feb 20, 2025
ee8bb09
add v1 feature flag
Feb 20, 2025
7a6dd94
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 20, 2025
05a11dd
resolve comments
Feb 20, 2025
4a5152a
clippy fix
Feb 21, 2025
abaf2c3
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 21, 2025
5c58cab
Merge branch 'main' into recovery_webhooks
srujanchikke Feb 22, 2025
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
48 changes: 44 additions & 4 deletions api-reference-v2/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -13397,6 +13397,19 @@
}
}
},
"PaymentAttemptFeatureMetadata": {
"type": "object",
"properties": {
"revenue_recovery": {
"allOf": [
{
"$ref": "#/components/schemas/PaymentAttemptRevenueRecoveryData"
}
],
"nullable": true
}
}
},
"PaymentAttemptResponse": {
"type": "object",
"required": [
Expand All @@ -13405,7 +13418,8 @@
"amount",
"authentication_type",
"created_at",
"modified_at"
"modified_at",
"connector_payment_id"
],
"properties": {
"id": {
Expand Down Expand Up @@ -13501,9 +13515,7 @@
},
"connector_payment_id": {
"type": "string",
"description": "A unique identifier for a payment provided by the connector",
"example": "993672945374576J",
"nullable": true
"description": "A unique identifier for a payment provided by the connector"
},
"payment_method_id": {
"type": "string",
Expand All @@ -13520,6 +13532,27 @@
"type": "string",
"description": "Value passed in X-CLIENT-VERSION header during payments confirm request by the client",
"nullable": true
},
"feature_metadata": {
"allOf": [
{
"$ref": "#/components/schemas/PaymentAttemptFeatureMetadata"
}
],
"nullable": true
}
}
},
"PaymentAttemptRevenueRecoveryData": {
"type": "object",
"properties": {
"attempt_triggered_by": {
"allOf": [
{
"$ref": "#/components/schemas/TriggeredBy"
}
],
"nullable": true
}
}
},
Expand Down Expand Up @@ -21526,6 +21559,13 @@
"payout"
]
},
"TriggeredBy": {
"type": "string",
"enum": [
"internal",
"external"
]
},
"UIWidgetFormLayout": {
"type": "string",
"enum": [
Expand Down
7 changes: 7 additions & 0 deletions api-reference/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -26144,6 +26144,13 @@
"payout"
]
},
"TriggeredBy": {
"type": "string",
"enum": [
"internal",
"external"
]
},
"UIWidgetFormLayout": {
"type": "string",
"enum": [
Expand Down
1 change: 1 addition & 0 deletions crates/api_models/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ customer_v2 = ["common_utils/customer_v2"]
payment_methods_v2 = ["common_utils/payment_methods_v2"]
dynamic_routing = []
control_center_theme = ["dep:actix-web", "dep:actix-multipart"]
revenue_recovery = []

[dependencies]
actix-multipart = { version = "0.6.1", optional = true }
Expand Down
42 changes: 40 additions & 2 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1511,8 +1511,8 @@ pub struct PaymentAttemptResponse {
pub payment_method_subtype: Option<api_enums::PaymentMethodType>,

/// A unique identifier for a payment provided by the connector
#[schema(value_type = Option<String>, example = "993672945374576J")]
pub connector_payment_id: Option<String>,
#[schema(value_type = String)]
pub connector_payment_id: Option<common_utils::types::ConnectorTransactionId>,

/// Identifier for Payment Method used for the payment attempt
#[schema(value_type = Option<String>, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
Expand All @@ -1522,6 +1522,24 @@ pub struct PaymentAttemptResponse {
pub client_source: Option<String>,
/// Value passed in X-CLIENT-VERSION header during payments confirm request by the client
pub client_version: Option<String>,

/// Additional data that might be required by hyperswitch, to enable some specific features.
pub feature_metadata: Option<PaymentAttemptFeatureMetadata>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)]
pub struct PaymentAttemptFeatureMetadata {
/// Revenue recovery metadata that might be required by hyperswitch.
pub revenue_recovery: Option<PaymentAttemptRevenueRecoveryData>,
}

#[cfg(feature = "v2")]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)]
pub struct PaymentAttemptRevenueRecoveryData {
/// Flag to find out whether an attempt was created by external or internal system.
#[schema(value_type = Option<TriggeredBy>, example = "internal")]
pub attempt_triggered_by: common_enums::TriggeredBy,
}

#[derive(
Expand Down Expand Up @@ -5563,6 +5581,26 @@ pub struct PaymentsRetrieveResponse {
pub attempts: Option<Vec<PaymentAttemptResponse>>,
}

#[cfg(feature = "v2")]
impl PaymentsRetrieveResponse {
pub fn find_attempt_in_attempts_list_using_connector_transaction_id(
self,
connector_transaction_id: &common_utils::types::ConnectorTransactionId,
) -> Option<PaymentAttemptResponse> {
self.attempts
.as_ref()
.and_then(|attempts| {
attempts.iter().find(|attempt| {
attempt
.connector_payment_id
.as_ref()
.is_some_and(|txn_id| txn_id == connector_transaction_id)
})
})
.cloned()
}
}

#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[cfg(feature = "v2")]
pub struct PaymentStartRedirectionRequest {
Expand Down
35 changes: 35 additions & 0 deletions crates/api_models/src/webhooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ pub enum IncomingWebhookEvent {
PayoutExpired,
#[cfg(feature = "payouts")]
PayoutReversed,
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
RecoveryPaymentFailure,
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
RecoveryPaymentSuccess,
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
RecoveryPaymentPending,
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
RecoveryInvoiceCancel,
}

pub enum WebhookFlow {
Expand All @@ -71,6 +79,8 @@ pub enum WebhookFlow {
Mandate,
ExternalAuthentication,
FraudCheck,
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
Recovery,
}

#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
Expand Down Expand Up @@ -197,6 +207,11 @@ impl From<IncomingWebhookEvent> for WebhookFlow {
| IncomingWebhookEvent::PayoutCreated
| IncomingWebhookEvent::PayoutExpired
| IncomingWebhookEvent::PayoutReversed => Self::Payout,
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
IncomingWebhookEvent::RecoveryInvoiceCancel
| IncomingWebhookEvent::RecoveryPaymentFailure
| IncomingWebhookEvent::RecoveryPaymentPending
| IncomingWebhookEvent::RecoveryPaymentSuccess => Self::Recovery,
}
}
}
Expand Down Expand Up @@ -236,6 +251,14 @@ pub enum ObjectReferenceId {
ExternalAuthenticationID(AuthenticationIdType),
#[cfg(feature = "payouts")]
PayoutId(PayoutIdType),
#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
InvoiceId(InvoiceIdType),
}

#[cfg(all(feature = "revenue_recovery", feature = "v2"))]
#[derive(Clone)]
pub enum InvoiceIdType {
ConnectorInvoiceId(String),
}

pub struct IncomingWebhookDetails {
Expand Down Expand Up @@ -303,3 +326,15 @@ pub struct ConnectorWebhookSecrets {
pub secret: Vec<u8>,
pub additional_secret: Option<masking::Secret<String>>,
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl IncomingWebhookEvent {
pub fn is_recovery_transaction_event(&self) -> bool {
matches!(
self,
Self::RecoveryPaymentFailure
| Self::RecoveryPaymentSuccess
| Self::RecoveryPaymentPending
)
}
}
25 changes: 25 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3000,9 +3000,9 @@
#[strum(serialize = "19")]
Correze,
#[strum(serialize = "20R")]
Corse,

Check warning on line 3003 in crates/common_enums/src/enums.rs

View workflow job for this annotation

GitHub Actions / Spell check

"Corse" should be "Course" or "Coarse" or "Core" or "Corpse" or "Cors" or "Corset" or "Curse" or "Horse" or "Norse" or "Torse" or "Worse".
#[strum(serialize = "2A")]
CorseDuSud,

Check warning on line 3005 in crates/common_enums/src/enums.rs

View workflow job for this annotation

GitHub Actions / Spell check

"Corse" should be "Course" or "Coarse" or "Core" or "Corpse" or "Cors" or "Corset" or "Curse" or "Horse" or "Norse" or "Torse" or "Worse".
#[strum(serialize = "21")]
CoteDor,
#[strum(serialize = "22")]
Expand Down Expand Up @@ -3032,7 +3032,7 @@
#[strum(serialize = "TF")]
FrenchSouthernAndAntarcticLands,
#[strum(serialize = "30")]
Gard,

Check warning on line 3035 in crates/common_enums/src/enums.rs

View workflow job for this annotation

GitHub Actions / Spell check

"Gard" should be "Guard".
#[strum(serialize = "32")]
Gers,
#[strum(serialize = "33")]
Expand All @@ -3044,7 +3044,7 @@
#[strum(serialize = "68")]
HautRhin,
#[strum(serialize = "2B")]
HauteCorse,

Check warning on line 3047 in crates/common_enums/src/enums.rs

View workflow job for this annotation

GitHub Actions / Spell check

"Corse" should be "Course" or "Coarse" or "Core" or "Corpse" or "Cors" or "Corset" or "Curse" or "Horse" or "Norse" or "Torse" or "Worse".
#[strum(serialize = "31")]
HauteGaronne,
#[strum(serialize = "43")]
Expand Down Expand Up @@ -3168,7 +3168,7 @@
#[strum(serialize = "93")]
SeineSaintDenis,
#[strum(serialize = "80")]
Somme,

Check warning on line 3171 in crates/common_enums/src/enums.rs

View workflow job for this annotation

GitHub Actions / Spell check

"Somme" should be "Some".
#[strum(serialize = "81")]
Tarn,
#[strum(serialize = "82")]
Expand Down Expand Up @@ -5896,7 +5896,7 @@
#[strum(serialize = "176")]
Razkrizje,
#[strum(serialize = "098")]
RaceFram,

Check warning on line 5899 in crates/common_enums/src/enums.rs

View workflow job for this annotation

GitHub Actions / Spell check

"Fram" should be "Frame".
#[strum(serialize = "201")]
RenčeVogrsko,
#[strum(serialize = "209")]
Expand Down Expand Up @@ -5960,7 +5960,7 @@
#[strum(serialize = "130")]
Trebnje,
#[strum(serialize = "185")]
TrnovskaVas,

Check warning on line 5963 in crates/common_enums/src/enums.rs

View workflow job for this annotation

GitHub Actions / Spell check

"Vas" should be "Was".
#[strum(serialize = "186")]
Trzin,
#[strum(serialize = "131")]
Expand Down Expand Up @@ -7591,3 +7591,28 @@
/// Payment Connector call succeeded
ConnectorCallSucceeded,
}

#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
PartialEq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumString,
ToSchema,
)]
#[router_derive::diesel_enum(storage_type = "db_enum")]
#[strum(serialize_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum TriggeredBy {
/// Denotes payment attempt is been created by internal system.
#[default]
Internal,
/// Denotes payment attempt is been created by external system.
External,
}
52 changes: 43 additions & 9 deletions crates/common_utils/src/ext_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
use error_stack::ResultExt;
use masking::{ExposeInterface, PeekInterface, Secret, Strategy};
use quick_xml::de;
#[cfg(all(feature = "logs", feature = "async_ext"))]
use router_env::logger;
use serde::{Deserialize, Serialize};

use crate::{
Expand Down Expand Up @@ -295,28 +297,34 @@ impl<T> StringExt<T> for String {
/// Extending functionalities of Wrapper types for idiomatic
#[cfg(feature = "async_ext")]
#[cfg_attr(feature = "async_ext", async_trait::async_trait)]
pub trait AsyncExt<A, B> {
pub trait AsyncExt<A> {
/// Output type of the map function
type WrappedSelf<T>;

/// Extending map by allowing functions which are async
async fn async_map<F, Fut>(self, func: F) -> Self::WrappedSelf<B>
async fn async_map<F, B, Fut>(self, func: F) -> Self::WrappedSelf<B>
where
F: FnOnce(A) -> Fut + Send,
Fut: futures::Future<Output = B> + Send;

/// Extending the `and_then` by allowing functions which are async
async fn async_and_then<F, Fut>(self, func: F) -> Self::WrappedSelf<B>
async fn async_and_then<F, B, Fut>(self, func: F) -> Self::WrappedSelf<B>
where
F: FnOnce(A) -> Fut + Send,
Fut: futures::Future<Output = Self::WrappedSelf<B>> + Send;

/// Extending `unwrap_or_else` to allow async fallback
async fn async_unwrap_or_else<F, Fut>(self, func: F) -> A
where
F: FnOnce() -> Fut + Send,
Fut: futures::Future<Output = A> + Send;
}

#[cfg(feature = "async_ext")]
#[cfg_attr(feature = "async_ext", async_trait::async_trait)]
impl<A: Send, B, E: Send> AsyncExt<A, B> for Result<A, E> {
impl<A: Send, E: Send + std::fmt::Debug> AsyncExt<A> for Result<A, E> {
type WrappedSelf<T> = Result<T, E>;
async fn async_and_then<F, Fut>(self, func: F) -> Self::WrappedSelf<B>
async fn async_and_then<F, B, Fut>(self, func: F) -> Self::WrappedSelf<B>
where
F: FnOnce(A) -> Fut + Send,
Fut: futures::Future<Output = Self::WrappedSelf<B>> + Send,
Expand All @@ -327,7 +335,7 @@ impl<A: Send, B, E: Send> AsyncExt<A, B> for Result<A, E> {
}
}

async fn async_map<F, Fut>(self, func: F) -> Self::WrappedSelf<B>
async fn async_map<F, B, Fut>(self, func: F) -> Self::WrappedSelf<B>
where
F: FnOnce(A) -> Fut + Send,
Fut: futures::Future<Output = B> + Send,
Expand All @@ -337,13 +345,28 @@ impl<A: Send, B, E: Send> AsyncExt<A, B> for Result<A, E> {
Err(err) => Err(err),
}
}

async fn async_unwrap_or_else<F, Fut>(self, func: F) -> A
where
F: FnOnce() -> Fut + Send,
Fut: futures::Future<Output = A> + Send,
{
match self {
Ok(a) => a,
Err(_err) => {
#[cfg(feature = "logs")]
logger::error!("Error: {:?}", _err);
func().await
}
}
}
}

#[cfg(feature = "async_ext")]
#[cfg_attr(feature = "async_ext", async_trait::async_trait)]
impl<A: Send, B> AsyncExt<A, B> for Option<A> {
impl<A: Send> AsyncExt<A> for Option<A> {
type WrappedSelf<T> = Option<T>;
async fn async_and_then<F, Fut>(self, func: F) -> Self::WrappedSelf<B>
async fn async_and_then<F, B, Fut>(self, func: F) -> Self::WrappedSelf<B>
where
F: FnOnce(A) -> Fut + Send,
Fut: futures::Future<Output = Self::WrappedSelf<B>> + Send,
Expand All @@ -354,7 +377,7 @@ impl<A: Send, B> AsyncExt<A, B> for Option<A> {
}
}

async fn async_map<F, Fut>(self, func: F) -> Self::WrappedSelf<B>
async fn async_map<F, B, Fut>(self, func: F) -> Self::WrappedSelf<B>
where
F: FnOnce(A) -> Fut + Send,
Fut: futures::Future<Output = B> + Send,
Expand All @@ -364,6 +387,17 @@ impl<A: Send, B> AsyncExt<A, B> for Option<A> {
None => None,
}
}

async fn async_unwrap_or_else<F, Fut>(self, func: F) -> A
where
F: FnOnce() -> Fut + Send,
Fut: futures::Future<Output = A> + Send,
{
match self {
Some(a) => a,
None => func().await,
}
}
}

/// Extension trait for validating application configuration. This trait provides utilities to
Expand Down
Loading
Loading