Bug: Paypal webhook disputes 




diff --git a/crates/router/src/connector/paypal.rs b/crates/router/src/connector/paypal.rs
index 456332ea0f1..ee6b6c818c5 100644
--- a/crates/router/src/connector/paypal.rs
+++ b/crates/router/src/connector/paypal.rs
@@ -1465,7 +1465,6 @@ impl
                 self, req, connectors,
             )?)
             .build();
-
         Ok(Some(request))
     }
 
@@ -1546,11 +1545,11 @@ impl api::IncomingWebhook for Paypal {
             }
             paypal::PaypalResource::PaypalDisputeWebhooks(resource) => {
                 Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
-                    api_models::payments::PaymentIdType::PaymentAttemptId(
+                    api_models::payments::PaymentIdType::ConnectorTransactionId(
                         resource
-                            .dispute_transactions
+                            .disputed_transactions
                             .first()
-                            .map(|transaction| transaction.reference_id.clone())
+                            .map(|transaction| transaction.seller_transaction_id.clone())
                             .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                     ),
                 ))
@@ -1567,17 +1566,17 @@ impl api::IncomingWebhook for Paypal {
             .parse_struct("PaypalWebooksEventType")
             .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
         let outcome = match payload.event_type {
-            PaypalWebhookEventType::CustomerDisputeCreated
-            | PaypalWebhookEventType::CustomerDisputeResolved
-            | PaypalWebhookEventType::CustomerDisputedUpdated
-            | PaypalWebhookEventType::RiskDisputeCreated => Some(
+            PaypalWebhookEventType::CustomerDisputeResolved => Some(
                 request
                     .body
                     .parse_struct::<paypal::DisputeOutcome>("PaypalWebooksEventType")
                     .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?
                     .outcome_code,
             ),
-            PaypalWebhookEventType::PaymentAuthorizationCreated
+            PaypalWebhookEventType::CustomerDisputeCreated
+            | PaypalWebhookEventType::RiskDisputeCreated
+            | PaypalWebhookEventType::CustomerDisputedUpdated
+            | PaypalWebhookEventType::PaymentAuthorizationCreated
             | PaypalWebhookEventType::PaymentAuthorizationVoided
             | PaypalWebhookEventType::PaymentCaptureDeclined
             | PaypalWebhookEventType::PaymentCaptureCompleted
@@ -1622,27 +1621,37 @@ impl api::IncomingWebhook for Paypal {
         &self,
         request: &api::IncomingWebhookRequestDetails<'_>,
     ) -> CustomResult<api::disputes::DisputePayload, errors::ConnectorError> {
-        let payload: paypal::PaypalDisputeWebhooks = request
+        let webhook_payload: paypal::PaypalWebhooksBody = request
             .body
-            .parse_struct("PaypalDisputeWebhooks")
+            .parse_struct("PaypalWebhooksBody")
             .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
-        Ok(api::disputes::DisputePayload {
-            amount: connector_utils::to_currency_lower_unit(
-                payload.dispute_amount.value.get_amount_as_string(),
-                payload.dispute_amount.currency_code,
-            )?,
-            currency: payload.dispute_amount.currency_code.to_string(),
-            dispute_stage: api_models::enums::DisputeStage::from(
-                payload.dispute_life_cycle_stage.clone(),
-            ),
-            connector_status: payload.status.to_string(),
-            connector_dispute_id: payload.dispute_id,
-            connector_reason: payload.reason,
-            connector_reason_code: payload.external_reason_code,
-            challenge_required_by: payload.seller_response_due_date,
-            created_at: payload.create_time,
-            updated_at: payload.update_time,
-        })
+        match webhook_payload.resource {
+            transformers::PaypalResource::PaypalCardWebhooks(_)
+            | transformers::PaypalResource::PaypalRedirectsWebhooks(_)
+            | transformers::PaypalResource::PaypalRefundWebhooks(_) => {
+                Err(errors::ConnectorError::ResponseDeserializationFailed)
+                    .attach_printable("Expected Dispute webhooks,but found other webhooks")?
+            }
+            transformers::PaypalResource::PaypalDisputeWebhooks(payload) => {
+                Ok(api::disputes::DisputePayload {
+                    amount: connector_utils::to_currency_lower_unit(
+                        payload.dispute_amount.value.get_amount_as_string(),
+                        payload.dispute_amount.currency_code,
+                    )?,
+                    currency: payload.dispute_amount.currency_code.to_string(),
+                    dispute_stage: api_models::enums::DisputeStage::from(
+                        payload.dispute_life_cycle_stage.clone(),
+                    ),
+                    connector_status: payload.status.to_string(),
+                    connector_dispute_id: payload.dispute_id,
+                    connector_reason: payload.reason.clone(),
+                    connector_reason_code: payload.reason,
+                    challenge_required_by: None,
+                    created_at: payload.create_time,
+                    updated_at: payload.update_time,
+                })
+            }
+        }
     }
 }
 
diff --git a/crates/router/src/connector/paypal/transformers.rs b/crates/router/src/connector/paypal/transformers.rs
index c12c7d5da15..39ea808d61f 100644
--- a/crates/router/src/connector/paypal/transformers.rs
+++ b/crates/router/src/connector/paypal/transformers.rs
@@ -1980,7 +1980,7 @@ pub struct OrderErrorDetails {
 
 #[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
 pub struct PaypalOrderErrorResponse {
-    pub name: String,
+    pub name: Option<String>,
     pub message: String,
     pub debug_id: Option<String>,
     pub details: Option<Vec<OrderErrorDetails>>,
@@ -1994,7 +1994,7 @@ pub struct ErrorDetails {
 
 #[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
 pub struct PaypalPaymentErrorResponse {
-    pub name: String,
+    pub name: Option<String>,
     pub message: String,
     pub debug_id: Option<String>,
     pub details: Option<Vec<ErrorDetails>>,
@@ -2056,21 +2056,24 @@ pub enum PaypalResource {
 #[derive(Deserialize, Debug, Serialize)]
 pub struct PaypalDisputeWebhooks {
     pub dispute_id: String,
-    pub dispute_transactions: Vec<DisputeTransaction>,
+    pub disputed_transactions: Vec<DisputeTransaction>,
     pub dispute_amount: OrderAmount,
-    pub dispute_outcome: DisputeOutcome,
+    pub dispute_outcome: Option<DisputeOutcome>,
     pub dispute_life_cycle_stage: DisputeLifeCycleStage,
     pub status: DisputeStatus,
     pub reason: Option<String>,
     pub external_reason_code: Option<String>,
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
     pub seller_response_due_date: Option<PrimitiveDateTime>,
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
     pub update_time: Option<PrimitiveDateTime>,
+    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
     pub create_time: Option<PrimitiveDateTime>,
 }
 
 #[derive(Deserialize, Debug, Serialize)]
 pub struct DisputeTransaction {
-    pub reference_id: String,
+    pub seller_transaction_id: String,
 }
 
 #[derive(Clone, Deserialize, Debug, strum::Display, Serialize)]

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

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

## Description
<!-- Describe your changes in detail -->
This refactored branch resolves the PayPal dispute webhook deserialization issue.
-> It was failing to identify the webhook entity type and object reference ID.
-> I updated the fields accordingly to resolve the deserialization issue.

### 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
Motivation https://github.com/juspay/hyperswitch/issues/5069

there are some filed refactors included which resolved the problem
<!--
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).
-->


## How did you test it?
I ran the ngrok for frowarding webhook payload to localhost with url (https://697b-219-65-110-2.ngrok-free.app/webhooks/merchant_1719300138/paypal)

I have created dispute webhook through paypal simulator and these are the logs :
Incoming webhook Payload:
```
{
  "id": "WH-4M0448861G563140B-9EX36365822141321",
  "create_time": "2018-06-21T13:36:33.000Z",
  "resource_type": "dispute",
  "event_type": "CUSTOMER.DISPUTE.CREATED",
  "summary": "A new dispute opened with Case # PP-000-042-663-135",
  "resource": {
    "disputed_transactions": [
      {
        "seller_transaction_id": "00D10444LD479031K",
        "seller": {
          "merchant_id": "RD465XN5VS364",
          "name": "Test Store"
        },
        "items": [],
        "seller_protection_eligible": true
      }
    ],
    "reason": "MERCHANDISE_OR_SERVICE_NOT_RECEIVED",
    "dispute_channel": "INTERNAL",
    "update_time": "2018-06-21T13:35:44.000Z",
    "create_time": "2018-06-21T13:35:44.000Z",
    "messages": [
      {
        "posted_by": "BUYER",
        "time_posted": "2018-06-21T13:35:52.000Z",
        "content": "qwqwqwq"
      }
    ],
    "links": [
      {
        "href": "https://api.paypal.com/v1/customer/disputes/PP-000-042-663-135",
        "rel": "self",
        "method": "GET"
      },
      {
        "href": "https://api.paypal.com/v1/customer/disputes/PP-000-042-663-135/send-message",
        "rel": "send_message",
        "method": "POST"
      }
    ],
    "dispute_amount": {
      "currency_code": "USD",
      "value": "3.00"
    },
    "dispute_id": "PP-000-042-663-135",
    "dispute_life_cycle_stage": "INQUIRY",
    "status": "OPEN"
  },
  "links": [
    {
      "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-4M0448861G563140B-9EX36365822141321",
      "rel": "self",
      "method": "GET",
      "encType": "application/json"
    },
    {
      "href": "https://api.paypal.com/v1/notifications/webhooks-events/WH-4M0448861G563140B-9EX36365822141321/resend",
      "rel": "resend",
      "method": "POST",
      "encType": "application/json"
    }
  ],
  "event_version": "1.0"
}
```

Dispute Creation:
<img width="1232" alt="image" src="https://github.com/juspay/hyperswitch/assets/60121719/d264c5dc-6a85-4c97-bb8f-4b83a5c88f48">


Source Verification:
Due to these are Mock events , we cant verify them through Paypal so I have done source verification as true to check further flows like Database record creation and triggering outgoing webhooks.

Database Record Creation:
<img width="1208" alt="image" src="https://github.com/juspay/hyperswitch/assets/60121719/dd0db6ae-5fd1-4e5c-b2a8-e12d30d2eb47">


Outgoing Webhook Response Payload:
```
{
    "merchant_id": "merchant_1719481316",
    "event_id": "evt_0190594e16ab7f38abd50ca6f32707b9",
    "event_type": "dispute_opened",
    "content": {
        "type": "dispute_details",
        "object": {
            "dispute_id": "dp_rgC7BN7DQRQ9YlksqCmO",
            "payment_id": "pay_qW4qVbLZXOO1GQsd0mQ3",
            "attempt_id": "00D10444LD479031K",
            "amount": "300",
            "currency": "USD",
            "dispute_stage": "pre_dispute",
            "dispute_status": "dispute_opened",
            "connector": "paypal",
            "connector_status": "Open",
            "connector_dispute_id": "PP-000-042-663-135",
            "connector_reason": "MERCHANDISE_OR_SERVICE_NOT_RECEIVED",
            "connector_reason_code": "MERCHANDISE_OR_SERVICE_NOT_RECEIVED",
            "challenge_required_by": null,
            "connector_created_at": "2018-06-21T13:35:44.000Z",
            "connector_updated_at": "2018-06-21T13:35:44.000Z",
            "created_at": "2024-06-27T10:46:48.214Z",
            "profile_id": "pro_AKqLxMihwY7h8QqvuPfH",
            "merchant_connector_id": "mca_q1lwLDnNEPzyGvClsHsR"
        }
    },
    "timestamp": "2024-06-27T10:46:48.235Z"
}
```

<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->


## 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
