Bug: [BUG]: [Adyen] Webhook Psync response Handler



### Bug Description

Currently the Psync response handler for webhook tightly coupled to payments response which is creating a lot of bugs in status and transaction id updates

- Update Psync response handler to consume the incoming webhook response
- Handle Webhook Status Mapping


### Expected Behavior

Decouple the Webhook Response from Payments response and handle all the status and reference ids separately 

### Actual Behavior

Currently the Psync response handler for webhook tightly coupled to payments response which is creating a lot of bugs in status and transaction id updates

- Update Psync response handler to consume the incoming webhook response
- Handle Webhook Status Mapping

### Steps To Reproduce

Provide an unambiguous set of steps to reproduce this bug. Include code or configuration to reproduce, if relevant.
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'


### Context For The Bug

_No response_

### Environment

Are you using hyperswitch hosted version? Yes/No
If yes, please provide the value of the `x-request-id` response header to help us debug your issue.

If not (or if building/running locally), please provide the following details:
1. Operating System or Linux distribution:
2. Rust version (output of `rustc --version`): ``
3. App version (output of `cargo r --features vergen -- --version`): ``


### Have you spent some time checking if this bug has been raised before?

- [X] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [X] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

Yes, I am willing to submit a PR!
diff --git a/crates/router/src/connector/adyen.rs b/crates/router/src/connector/adyen.rs
index 93301364e62..67e319ddc22 100644
--- a/crates/router/src/connector/adyen.rs
+++ b/crates/router/src/connector/adyen.rs
@@ -403,6 +403,13 @@ impl
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 impl api::PaymentSession for Adyen {}
@@ -516,6 +523,13 @@ impl
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 /// Payment Sync can be useful only incase of Redirect flow.
@@ -681,6 +695,13 @@ impl
     ) -> CustomResult<services::CaptureSyncMethod, errors::ConnectorError> {
         Ok(services::CaptureSyncMethod::Individual)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 impl
@@ -795,6 +816,14 @@ impl
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 impl api::PaymentsPreProcessing for Adyen {}
@@ -925,6 +954,14 @@ impl
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 impl
@@ -1022,6 +1059,13 @@ impl
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 impl api::Payouts for Adyen {}
@@ -1129,6 +1173,13 @@ impl services::ConnectorIntegration<api::PoCancel, types::PayoutsData, types::Pa
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 #[cfg(feature = "payouts")]
@@ -1227,6 +1278,13 @@ impl services::ConnectorIntegration<api::PoCreate, types::PayoutsData, types::Pa
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 #[cfg(feature = "payouts")]
@@ -1330,6 +1388,13 @@ impl
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 #[cfg(feature = "payouts")]
@@ -1446,6 +1511,13 @@ impl services::ConnectorIntegration<api::PoFulfill, types::PayoutsData, types::P
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 impl api::Refund for Adyen {}
@@ -1553,6 +1625,13 @@ impl services::ConnectorIntegration<api::Execute, types::RefundsData, types::Ref
     ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
         self.build_error_response(res, event_builder)
     }
+    fn get_5xx_error_response(
+        &self,
+        res: types::Response,
+        event_builder: Option<&mut ConnectorEvent>,
+    ) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
+        self.build_error_response(res, event_builder)
+    }
 }
 
 impl services::ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData>
@@ -1717,7 +1796,7 @@ impl api::IncomingWebhook for Adyen {
         let notif = get_webhook_object_from_body(request.body)
             .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
 
-        let response: adyen::Response = notif.into();
+        let response = adyen::AdyenWebhookResponse::from(notif);
 
         Ok(Box::new(response))
     }
diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs
index 9fbf7d99812..c44d590e2f1 100644
--- a/crates/router/src/connector/adyen/transformers.rs
+++ b/crates/router/src/connector/adyen/transformers.rs
@@ -3,7 +3,7 @@ use api_models::payouts::PayoutMethodData;
 use api_models::{enums, payments, webhooks};
 use cards::CardNumber;
 use common_utils::{ext_traits::Encode, pii};
-use error_stack::ResultExt;
+use error_stack::{IntoReport, ResultExt};
 use masking::{ExposeInterface, PeekInterface};
 use reqwest::Url;
 use serde::{Deserialize, Serialize};
@@ -24,7 +24,7 @@ use crate::{
         self,
         api::{self, enums as api_enums},
         storage::enums as storage_enums,
-        transformers::ForeignFrom,
+        transformers::{ForeignFrom, ForeignTryFrom},
         PaymentsAuthorizeData,
     },
     utils as crate_utils,
@@ -271,6 +271,31 @@ impl ForeignFrom<(bool, AdyenStatus, Option<common_enums::PaymentMethodType>)>
     }
 }
 
+impl ForeignTryFrom<(bool, AdyenWebhookStatus)> for storage_enums::AttemptStatus {
+    type Error = error_stack::Report<errors::ConnectorError>;
+    fn foreign_try_from(
+        (is_manual_capture, adyen_webhook_status): (bool, AdyenWebhookStatus),
+    ) -> Result<Self, Self::Error> {
+        match adyen_webhook_status {
+            AdyenWebhookStatus::Authorised => match is_manual_capture {
+                true => Ok(Self::Authorized),
+                // In case of Automatic capture Authorized is the final status of the payment
+                false => Ok(Self::Charged),
+            },
+            AdyenWebhookStatus::AuthorisationFailed => Ok(Self::Failure),
+            AdyenWebhookStatus::Cancelled => Ok(Self::Voided),
+            AdyenWebhookStatus::CancelFailed => Ok(Self::VoidFailed),
+            AdyenWebhookStatus::Captured => Ok(Self::Charged),
+            AdyenWebhookStatus::CaptureFailed => Ok(Self::CaptureFailed),
+            //If Unexpected Event is received, need to understand how it reached this point
+            //Webhooks with Payment Events only should try to conume this resource object.
+            AdyenWebhookStatus::UnexpectedEvent => {
+                Err(errors::ConnectorError::WebhookBodyDecodingFailed).into_report()
+            }
+        }
+    }
+}
+
 #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
 pub struct AdyenRedirectRequest {
     pub details: AdyenRedirectRequestTypes,
@@ -320,6 +345,7 @@ pub enum AdyenPaymentResponse {
     QrCodeResponse(Box<QrCodeResponseResponse>),
     RedirectionResponse(Box<RedirectionResponse>),
     RedirectionErrorResponse(Box<RedirectionErrorResponse>),
+    WebhookResponse(Box<AdyenWebhookResponse>),
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -332,8 +358,31 @@ pub struct Response {
     refusal_reason: Option<String>,
     refusal_reason_code: Option<String>,
     additional_data: Option<AdditionalData>,
-    // event_code will be available only in webhook body
-    event_code: Option<WebhookEventCode>,
+}
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub enum AdyenWebhookStatus {
+    Authorised,
+    AuthorisationFailed,
+    Cancelled,
+    CancelFailed,
+    Captured,
+    CaptureFailed,
+    UnexpectedEvent,
+}
+
+//Creating custom struct which can be consumed in Psync Handler triggered from Webhooks
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct AdyenWebhookResponse {
+    transaction_id: String,
+    payment_reference: Option<String>,
+    status: AdyenWebhookStatus,
+    amount: Option<Amount>,
+    merchant_reference_id: String,
+    refusal_reason: Option<String>,
+    refusal_reason_code: Option<String>,
+    event_code: WebhookEventCode,
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -3091,7 +3140,6 @@ pub fn get_adyen_response(
 > {
     let status =
         storage_enums::AttemptStatus::foreign_from((is_capture_manual, response.result_code, pmt));
-    let status = update_attempt_status_based_on_event_type_if_needed(status, &response.event_code);
     let error = if response.refusal_reason.is_some() || response.refusal_reason_code.is_some() {
         Some(types::ErrorResponse {
             code: response
@@ -3135,10 +3183,11 @@ pub fn get_adyen_response(
     Ok((status, error, payments_response_data))
 }
 
-pub fn get_adyen_response_for_multiple_partial_capture(
-    response: Response,
+pub fn get_webhook_response(
+    response: AdyenWebhookResponse,
+    is_capture_manual: bool,
+    is_multiple_capture_psync_flow: bool,
     status_code: u16,
-    pmt: Option<enums::PaymentMethodType>,
 ) -> errors::CustomResult<
     (
         storage_enums::AttemptStatus,
@@ -3147,28 +3196,53 @@ pub fn get_adyen_response_for_multiple_partial_capture(
     ),
     errors::ConnectorError,
 > {
-    let (status, error, _) = get_adyen_response(response.clone(), true, status_code, pmt)?;
-    let status = update_attempt_status_based_on_event_type_if_needed(status, &response.event_code);
-    let capture_sync_response_list = utils::construct_captures_response_hashmap(vec![response]);
-    Ok((
-        status,
-        error,
-        types::PaymentsResponseData::MultipleCaptureResponse {
-            capture_sync_response_list,
-        },
-    ))
-}
+    let status = storage_enums::AttemptStatus::foreign_try_from((
+        is_capture_manual,
+        response.status.clone(),
+    ))?;
+    let error = if response.refusal_reason.is_some() || response.refusal_reason_code.is_some() {
+        Some(types::ErrorResponse {
+            code: response
+                .refusal_reason_code
+                .clone()
+                .unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
+            message: response
+                .refusal_reason
+                .clone()
+                .unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
+            reason: response.refusal_reason.clone(),
+            status_code,
+            attempt_status: None,
+            connector_transaction_id: Some(response.transaction_id.clone()),
+        })
+    } else {
+        None
+    };
 
-fn update_attempt_status_based_on_event_type_if_needed(
-    status: storage_enums::AttemptStatus,
-    event: &Option<WebhookEventCode>,
-) -> storage_enums::AttemptStatus {
-    if status == storage_enums::AttemptStatus::Authorized
-        && event == &Some(WebhookEventCode::Capture)
-    {
-        storage_enums::AttemptStatus::Charged
+    if is_multiple_capture_psync_flow {
+        let capture_sync_response_list = utils::construct_captures_response_hashmap(vec![response]);
+        Ok((
+            status,
+            error,
+            types::PaymentsResponseData::MultipleCaptureResponse {
+                capture_sync_response_list,
+            },
+        ))
     } else {
-        status
+        let payments_response_data = types::PaymentsResponseData::TransactionResponse {
+            resource_id: types::ResponseId::ConnectorTransactionId(
+                response
+                    .payment_reference
+                    .unwrap_or(response.transaction_id),
+            ),
+            redirection_data: None,
+            mandate_reference: None,
+            connector_metadata: None,
+            network_txn_id: None,
+            connector_response_reference_id: Some(response.merchant_reference_id),
+            incremental_authorization_allowed: None,
+        };
+        Ok((status, error, payments_response_data))
     }
 }
 
@@ -3656,11 +3730,7 @@ impl<F, Req>
         let is_manual_capture = utils::is_manual_capture(capture_method);
         let (status, error, payment_response_data) = match item.response {
             AdyenPaymentResponse::Response(response) => {
-                if is_multiple_capture_psync_flow {
-                    get_adyen_response_for_multiple_partial_capture(*response, item.http_code, pmt)?
-                } else {
-                    get_adyen_response(*response, is_manual_capture, item.http_code, pmt)?
-                }
+                get_adyen_response(*response, is_manual_capture, item.http_code, pmt)?
             }
             AdyenPaymentResponse::PresentToShopper(response) => {
                 get_present_to_shopper_response(*response, is_manual_capture, item.http_code, pmt)?
@@ -3674,6 +3744,12 @@ impl<F, Req>
             AdyenPaymentResponse::RedirectionErrorResponse(response) => {
                 get_redirection_error_response(*response, is_manual_capture, item.http_code, pmt)?
             }
+            AdyenPaymentResponse::WebhookResponse(response) => get_webhook_response(
+                *response,
+                is_manual_capture,
+                is_multiple_capture_psync_flow,
+                item.http_code,
+            )?,
         };
 
         Ok(Self {
@@ -3893,6 +3969,8 @@ pub enum WebhookEventCode {
     Refund,
     CancelOrRefund,
     Cancellation,
+    Capture,
+    CaptureFailed,
     RefundFailed,
     RefundReversed,
     NotificationOfChargeback,
@@ -3901,8 +3979,6 @@ pub enum WebhookEventCode {
     SecondChargeback,
     PrearbitrationWon,
     PrearbitrationLost,
-    Capture,
-    CaptureFailed,
     #[serde(other)]
     Unknown,
 }
@@ -4046,52 +4122,81 @@ pub struct AdyenIncomingWebhook {
     pub notification_items: Vec<AdyenItemObjectWH>,
 }
 
-impl From<AdyenNotificationRequestItemWH> for Response {
+impl From<AdyenNotificationRequestItemWH> for AdyenWebhookResponse {
     fn from(notif: AdyenNotificationRequestItemWH) -> Self {
         Self {
-            psp_reference: notif.psp_reference,
-            merchant_reference: notif.merchant_reference,
-            result_code: match notif.success.as_str() {
-                "true" => {
-                    if notif.event_code == WebhookEventCode::Cancellation {
-                        AdyenStatus::Cancelled
+            transaction_id: notif.psp_reference,
+            payment_reference: notif.original_reference,
+            //Translating into custom status so that it can be clearly mapped to out attempt_status
+            status: match notif.event_code {
+                WebhookEventCode::Authorisation => {
+                    if is_success_scenario(notif.success) {
+                        AdyenWebhookStatus::Authorised
                     } else {
-                        AdyenStatus::Authorised
+                        AdyenWebhookStatus::AuthorisationFailed
                     }
                 }
-                _ => AdyenStatus::Refused,
+                WebhookEventCode::Cancellation => {
+                    if is_success_scenario(notif.success) {
+                        AdyenWebhookStatus::Cancelled
+                    } else {
+                        AdyenWebhookStatus::CancelFailed
+                    }
+                }
+                WebhookEventCode::Capture => {
+                    if is_success_scenario(notif.success) {
+                        AdyenWebhookStatus::Captured
+                    } else {
+                        AdyenWebhookStatus::CaptureFailed
+                    }
+                }
+                WebhookEventCode::CaptureFailed => AdyenWebhookStatus::CaptureFailed,
+                WebhookEventCode::CancelOrRefund
+                | WebhookEventCode::Refund
+                | WebhookEventCode::RefundFailed
+                | WebhookEventCode::RefundReversed
+                | WebhookEventCode::NotificationOfChargeback
+                | WebhookEventCode::Chargeback
+                | WebhookEventCode::ChargebackReversed
+                | WebhookEventCode::SecondChargeback
+                | WebhookEventCode::PrearbitrationWon
+                | WebhookEventCode::PrearbitrationLost
+                | WebhookEventCode::Unknown => AdyenWebhookStatus::UnexpectedEvent,
             },
             amount: Some(Amount {
                 value: notif.amount.value,
                 currency: notif.amount.currency,
             }),
+            merchant_reference_id: notif.merchant_reference,
             refusal_reason: None,
             refusal_reason_code: None,
-            additional_data: None,
-            event_code: Some(notif.event_code),
+            event_code: notif.event_code,
         }
     }
 }
 
-impl utils::MultipleCaptureSyncResponse for Response {
+//This will be triggered in Psync handler of webhook response
+impl utils::MultipleCaptureSyncResponse for AdyenWebhookResponse {
     fn get_connector_capture_id(&self) -> String {
-        self.psp_reference.clone()
+        self.transaction_id.clone()
     }
 
     fn get_capture_attempt_status(&self) -> enums::AttemptStatus {
-        match self.result_code {
-            AdyenStatus::Authorised => enums::AttemptStatus::Charged,
+        match self.status {
+            AdyenWebhookStatus::Captured => enums::AttemptStatus::Charged,
             _ => enums::AttemptStatus::CaptureFailed,
         }
     }
 
     fn is_capture_response(&self) -> bool {
-        self.event_code == Some(WebhookEventCode::Capture)
-            || self.event_code == Some(WebhookEventCode::CaptureFailed)
+        matches!(
+            self.event_code,
+            WebhookEventCode::Capture | WebhookEventCode::CaptureFailed
+        )
     }
 
     fn get_connector_reference_id(&self) -> Option<String> {
-        Some(self.merchant_reference.clone())
+        Some(self.merchant_reference_id.clone())
     }
 
     fn get_amount_captured(&self) -> Option<i64> {

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [X] Bugfix
- [ ] New feature
- [ ] Enhancement
- [X] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
- Update Psync response handler to consume the incoming webhook response
- Handle Webhook Status Mapping

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->
#3977 

## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
For Adyen all the subsequent payments are confirmed via Webhooks, So test Capture, Cancel, Refunds and Wallets payments.
Payment Methods Need to be tested

- Klarna (Manual Capture)
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_ooWwJsn2dIWX2JrPdAbV6SYbxVBF5W0LY2ZvWHV2U3F5UXRQdHDAm9zt5q7Hk0HL' \
--data-raw '{
    "amount": 6540,
    "currency": "EUR",
    "confirm": true,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "customer_id": "StripeCustomer",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "profile_id": "pro_doSItPZQZBbfoS0iEf81",
    "business_country": "US",
    "payment_method": "pay_later",
    "payment_method_type": "klarna",
    "payment_experience": "redirect_to_url",
    "connector": [
        "adyen"
    ],
    "payment_method_data": {
        "pay_later": {
            "klarna_redirect": {
                "billing_email": "hyperswitch_sdk_demo_id@gmail.com",
                "billing_country": "DE"
            }
        }
    },
    "routing": {
        "type": "single",
        "data": "adyen"
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "DE",
            "first_name": "PiX"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
```
- Paypal (Manual Capture)
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_ooWwJsn2dIWX2JrPdAbV6SYbxVBF5W0LY2ZvWHV2U3F5UXRQdHDAm9zt5q7Hk0HL' \
--data-raw '{
    "amount": 6540,
    "currency": "USD",
    "confirm": true,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "customer_id": "StripeCustomer",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "profile_id": "pro_doSItPZQZBbfoS0iEf81",
    "business_country": "US",
    "payment_method": "wallet",
    "payment_method_type": "paypal",
    "payment_method_data": {
        "wallet": {
            "paypal_redirect": {}
        }
    },
    "routing": {
        "type": "single",
        "data": "adyen"
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "DE",
            "first_name": "PiX"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
```
- Cards (Automatic and Manual)
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_ooWwJsn2dIWX2JrPdAbV6SYbxVBF5W0LY2ZvWHV2U3F5UXRQdHDAm9zt5q7Hk0HL' \
--data-raw '{
    "amount": 6540,
    "currency": "USD",
    "confirm": true,
    "capture_method": "manual",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 6540,
    "customer_id": "StripeCustomer",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://duck.com",
    "profile_id": "pro_doSItPZQZBbfoS0iEf81",
    "business_country": "US",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number": "371449635398431",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "7373"
        }
    },
    "routing": {
        "type": "single",
        "data": "adyen"
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "DE",
            "first_name": "PiX"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "PiX"
        }
    },
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'
```
- ideal (Automatic Capture)
```
**Confirm Call:**
{
    "client_secret": "{{client_secret}}",
    "payment_method": "bank_redirect",
    "payment_method_type": "ideal",
    "payment_method_data": {
        "bank_redirect": {
            "ideal": {
                "billing_details": {
                    "billing_name": "John Doe"
                },
                "bank_name": "n26",
                "preferred_language": "en",
                "country": "NL"
            }
        }
    },
    "browser_info": {
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36",
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "language": "nl-NL",
        "color_depth": 24,
        "screen_height": 723,
        "screen_width": 1536,
        "time_zone": 0,
        "java_enabled": true,
        "java_script_enabled": true,
        "ip_address": "127.0.0.1"
    }
}
```
- Do any of the operation `REFUND, CANCEL, CAPTURE`
- Wait for Configured webhooks
- Do a sync for Success Scenario


**Outgoing Webhooks Example**

- Charged
<details><summary> Cards </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/47c3d2c1-3e13-4490-b54a-220ce4fe3873">
</details> 

<details><summary> Paypal </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/e4b32b2e-152a-4f75-8559-8e26994528d6">
</details> 

<details><summary> Klarna </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/62851be5-2b37-4d18-933c-cec2f956a6d8">
</details> 

<details><summary> Ideal </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/dec43f75-4859-43fe-a296-3a76362ec144">
</details> 


- Authorized
<details><summary> Cards </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/6163f6fc-59ad-45da-86ee-9f847b5db5de">
</details> 

<details><summary> Paypal </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/94a1b125-06c1-4f47-9feb-37c551581bcf">
</details> 

<details><summary> Klarna </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/1875945d-e76c-43b8-9597-8dd9b89f7e76">
</details> 
<details><summary> Cancelled </summary>
<img src = "https://github.com/juspay/hyperswitch/assets/55536657/1f24c3de-5e1a-4f13-aa72-4ac2f74b2a86">
</details> 
<details><summary> Captured </summary>
<img src = "(https://github.com/juspay/hyperswitch/assets/55536657/e5d83e48-b70e-46f3-a815-6278878831a7">
</details> 
<details><summary> Refunded </summary>
<img src = "(https://github.com/juspay/hyperswitch/assets/55536657/fef683dd-b4e2-498a-bc9a-6ef32206ef6c">
</details> 

## Checklist
<!-- Put an `x` in the boxes that apply -->

- [X] I formatted the code `cargo +nightly fmt --all`
- [X] I addressed lints thrown by `cargo clippy`
- [X] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable