From df51a4ff9430583de205919c54be6bcb71680280 Mon Sep 17 00:00:00 2001 From: Aishwariyaa Anand <124241367+Aishwariyaa-Anand@users.noreply.github.com> Date: Wed, 8 Oct 2025 19:44:06 +0530 Subject: [PATCH] refactor(core): add webhook_url for v2 tunnel (#9625) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference/v2/openapi_spec_v2.json | 12 ++++++++++++ crates/api_models/src/payments.rs | 9 +++++++++ crates/hyperswitch_domain_models/src/payments.rs | 2 ++ crates/router/src/core/payment_methods.rs | 1 + .../external_vault_proxy_payment_intent.rs | 1 + .../payments/operations/payment_confirm_intent.rs | 4 ++++ .../payments/operations/proxy_payments_intent.rs | 1 + crates/router/src/core/payments/transformers.rs | 13 +++++++------ 8 files changed, 37 insertions(+), 6 deletions(-) diff --git a/api-reference/v2/openapi_spec_v2.json b/api-reference/v2/openapi_spec_v2.json index 434bbd298b..1bbeb5f04c 100644 --- a/api-reference/v2/openapi_spec_v2.json +++ b/api-reference/v2/openapi_spec_v2.json @@ -19133,6 +19133,12 @@ "type": "boolean", "description": "If true, returns stringified connector raw response body", "nullable": true + }, + "webhook_url": { + "type": "string", + "description": "The webhook endpoint URL to receive payment status notifications", + "example": "https://merchant.example.com/webhooks/payment", + "nullable": true } }, "additionalProperties": false @@ -20186,6 +20192,12 @@ "description": "Allow partial authorization for this payment", "default": false, "nullable": true + }, + "webhook_url": { + "type": "string", + "description": "The webhook endpoint URL to receive payment status notifications", + "example": "https://merchant.example.com/webhooks/payment", + "nullable": true } }, "additionalProperties": false diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index ecc34cbd43..dfc2808814 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -5942,6 +5942,10 @@ pub struct PaymentsConfirmIntentRequest { /// If true, returns stringified connector raw response body pub return_raw_connector_response: Option, + + /// The webhook endpoint URL to receive payment status notifications + #[schema(value_type = Option, example = "https://merchant.example.com/webhooks/payment")] + pub webhook_url: Option, } // Serialize is implemented because, this will be serialized in the api events. @@ -6182,6 +6186,10 @@ pub struct PaymentsRequest { /// Allow partial authorization for this payment #[schema(value_type = Option, default = false)] pub enable_partial_authorization: Option, + + /// The webhook endpoint URL to receive payment status notifications + #[schema(value_type = Option, example = "https://merchant.example.com/webhooks/payment")] + pub webhook_url: Option, } #[cfg(feature = "v2")] @@ -6237,6 +6245,7 @@ impl From<&PaymentsRequest> for PaymentsConfirmIntentRequest { merchant_connector_details: request.merchant_connector_details.clone(), return_raw_connector_response: request.return_raw_connector_response, split_payment_method_data: None, + webhook_url: request.webhook_url.clone(), } } } diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs index b1f7a4745e..a01efdf074 100644 --- a/crates/hyperswitch_domain_models/src/payments.rs +++ b/crates/hyperswitch_domain_models/src/payments.rs @@ -944,6 +944,8 @@ where pub payment_method: Option, pub merchant_connector_details: Option, pub external_vault_pmd: Option, + /// The webhook url of the merchant, to which the connector will send the webhook. + pub webhook_url: Option, } #[cfg(feature = "v2")] diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 72c91ca43d..f1f5c482c3 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -3438,6 +3438,7 @@ fn construct_zero_auth_payments_request( merchant_connector_details: None, return_raw_connector_response: None, enable_partial_authorization: None, + webhook_url: None, }) } diff --git a/crates/router/src/core/payments/operations/external_vault_proxy_payment_intent.rs b/crates/router/src/core/payments/operations/external_vault_proxy_payment_intent.rs index 7d40bb81c2..4e4302b1d5 100644 --- a/crates/router/src/core/payments/operations/external_vault_proxy_payment_intent.rs +++ b/crates/router/src/core/payments/operations/external_vault_proxy_payment_intent.rs @@ -298,6 +298,7 @@ impl GetTracker, ExternalVaultP payment_method: None, merchant_connector_details: None, external_vault_pmd: payment_method_data, + webhook_url: None, }; let get_trackers_response = operations::GetTrackerResponse { payment_data }; diff --git a/crates/router/src/core/payments/operations/payment_confirm_intent.rs b/crates/router/src/core/payments/operations/payment_confirm_intent.rs index 242ff3a425..f7db988b26 100644 --- a/crates/router/src/core/payments/operations/payment_confirm_intent.rs +++ b/crates/router/src/core/payments/operations/payment_confirm_intent.rs @@ -276,6 +276,10 @@ impl GetTracker, PaymentsConfir payment_method: None, merchant_connector_details, external_vault_pmd: None, + webhook_url: request + .webhook_url + .as_ref() + .map(|url| url.get_string_repr().to_string()), }; let get_trackers_response = operations::GetTrackerResponse { payment_data }; diff --git a/crates/router/src/core/payments/operations/proxy_payments_intent.rs b/crates/router/src/core/payments/operations/proxy_payments_intent.rs index c53aff42b5..ceae5d850f 100644 --- a/crates/router/src/core/payments/operations/proxy_payments_intent.rs +++ b/crates/router/src/core/payments/operations/proxy_payments_intent.rs @@ -269,6 +269,7 @@ impl GetTracker, ProxyPaymentsR payment_method: None, merchant_connector_details: None, external_vault_pmd: None, + webhook_url: None, }; let get_trackers_response = operations::GetTrackerResponse { payment_data }; diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index e0859f7642..b740ca8f28 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -334,8 +334,9 @@ pub async fn construct_payment_router_data_for_authorize<'a>( &attempt.merchant_id, merchant_connector_account.get_id().get_string_repr(), )), - // TODO: Implement for connectors that require a webhook URL to be included in the request payload. - domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None, + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => { + payment_data.webhook_url + } }; let router_return_url = payment_data @@ -587,8 +588,9 @@ pub async fn construct_external_vault_proxy_payment_router_data<'a>( &attempt.merchant_id, merchant_connector_account.get_id().get_string_repr(), )), - // TODO: Implement for connectors that require a webhook URL to be included in the request payload. - domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => None, + domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => { + payment_data.webhook_url.clone() + } }; let router_return_url = payment_data @@ -1432,9 +1434,8 @@ pub async fn construct_payment_router_data_for_setup_mandate<'a>( &attempt.merchant_id, merchant_connector_account.get_id().get_string_repr(), )), - // TODO: Implement for connectors that require a webhook URL to be included in the request payload. domain::MerchantConnectorAccountTypeDetails::MerchantConnectorDetails(_) => { - todo!("Add webhook URL to request for this connector") + payment_data.webhook_url } };