Bug: [FEATURE] [CRYPTOPAY] Report underpaid/overpaid amount in outgoing webhooks



### Feature Description

During settlement, payments processed through the Cryptopay connector may experience underpayment or overpayment, leading to fluctuations in the payment amount. The revised amount must be communicated through outgoing webhooks for the respective payment.

### Possible Implementation

The new `price_amount` should be populated in `amount_captured` field.

### Have you spent some time checking if this feature request 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/cryptopay.rs b/crates/router/src/connector/cryptopay.rs
index 2f9c996eff4..7245694bd44 100644
--- a/crates/router/src/connector/cryptopay.rs
+++ b/crates/router/src/connector/cryptopay.rs
@@ -287,11 +287,14 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
         event_builder.map(|i| i.set_response_body(&response));
         router_env::logger::info!(connector_response=?response);
-        types::RouterData::try_from(types::ResponseRouterData {
-            response,
-            data: data.clone(),
-            http_code: res.status_code,
-        })
+        types::RouterData::try_from((
+            types::ResponseRouterData {
+                response,
+                data: data.clone(),
+                http_code: res.status_code,
+            },
+            data.request.currency,
+        ))
     }
 
     fn get_error_response(
@@ -371,11 +374,14 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
             .change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
         event_builder.map(|i| i.set_response_body(&response));
         router_env::logger::info!(connector_response=?response);
-        types::RouterData::try_from(types::ResponseRouterData {
-            response,
-            data: data.clone(),
-            http_code: res.status_code,
-        })
+        types::RouterData::try_from((
+            types::ResponseRouterData {
+                response,
+                data: data.clone(),
+                http_code: res.status_code,
+            },
+            data.request.currency,
+        ))
     }
 
     fn get_error_response(
diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs
index e6db0cf320c..b8f398a0d96 100644
--- a/crates/router/src/connector/cryptopay/transformers.rs
+++ b/crates/router/src/connector/cryptopay/transformers.rs
@@ -1,8 +1,10 @@
 use common_utils::pii;
+use error_stack::ResultExt;
 use masking::Secret;
 use reqwest::Url;
 use serde::{Deserialize, Serialize};
 
+use super::utils as connector_utils;
 use crate::{
     connector::utils::{self, is_payment_failure, CryptoData, PaymentsAuthorizeRequestData},
     consts,
@@ -146,17 +148,17 @@ pub struct CryptopayPaymentsResponse {
 }
 
 impl<F, T>
-    TryFrom<types::ResponseRouterData<F, CryptopayPaymentsResponse, T, types::PaymentsResponseData>>
-    for types::RouterData<F, T, types::PaymentsResponseData>
+    TryFrom<(
+        types::ResponseRouterData<F, CryptopayPaymentsResponse, T, types::PaymentsResponseData>,
+        diesel_models::enums::Currency,
+    )> for types::RouterData<F, T, types::PaymentsResponseData>
 {
     type Error = error_stack::Report<errors::ConnectorError>;
     fn try_from(
-        item: types::ResponseRouterData<
-            F,
-            CryptopayPaymentsResponse,
-            T,
-            types::PaymentsResponseData,
-        >,
+        (item, currency): (
+            types::ResponseRouterData<F, CryptopayPaymentsResponse, T, types::PaymentsResponseData>,
+            diesel_models::enums::Currency,
+        ),
     ) -> Result<Self, Self::Error> {
         let status = enums::AttemptStatus::from(item.response.data.status.clone());
         let response = if is_payment_failure(status) {
@@ -197,11 +199,28 @@ impl<F, T>
                 incremental_authorization_allowed: None,
             })
         };
-        Ok(Self {
-            status,
-            response,
-            ..item.data
-        })
+
+        match item.response.data.price_amount {
+            Some(price_amount) => {
+                let amount_captured = Some(
+                    connector_utils::to_currency_lower_unit(price_amount, currency)?
+                        .parse::<i64>()
+                        .change_context(errors::ConnectorError::ParsingFailed)?,
+                );
+
+                Ok(Self {
+                    status,
+                    response,
+                    amount_captured,
+                    ..item.data
+                })
+            }
+            None => Ok(Self {
+                status,
+                response,
+                ..item.data
+            }),
+        }
     }
 }
 
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index 23b3f1f6931..c8de432fc0c 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -1220,6 +1220,7 @@ impl<F: Clone> TryFrom<PaymentAdditionalData<'_, F>> for types::PaymentsSyncData
                 None => types::SyncRequestType::SinglePaymentSync,
             },
             payment_method_type: payment_data.payment_attempt.payment_method_type,
+            currency: payment_data.currency,
         })
     }
 }
diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs
index 6004131be40..951be887ed1 100644
--- a/crates/router/src/types.rs
+++ b/crates/router/src/types.rs
@@ -565,6 +565,7 @@ pub struct PaymentsSyncData {
     pub sync_type: SyncRequestType,
     pub mandate_id: Option<api_models::payments::MandateIds>,
     pub payment_method_type: Option<storage_enums::PaymentMethodType>,
+    pub currency: storage_enums::Currency,
 }
 
 #[derive(Debug, Default, Clone)]
diff --git a/crates/router/tests/connectors/bambora.rs b/crates/router/tests/connectors/bambora.rs
index 46f61271ee4..5cd82d097aa 100644
--- a/crates/router/tests/connectors/bambora.rs
+++ b/crates/router/tests/connectors/bambora.rs
@@ -109,6 +109,7 @@ async fn should_sync_authorized_payment() {
                 sync_type: types::SyncRequestType::SinglePaymentSync,
                 connector_meta: None,
                 payment_method_type: None,
+                currency: enums::Currency::USD,
             }),
             None,
         )
@@ -224,6 +225,7 @@ async fn should_sync_auto_captured_payment() {
                 sync_type: types::SyncRequestType::SinglePaymentSync,
                 connector_meta: None,
                 payment_method_type: None,
+                currency: enums::Currency::USD,
             }),
             None,
         )
diff --git a/crates/router/tests/connectors/forte.rs b/crates/router/tests/connectors/forte.rs
index c7934b3711a..c58cab38a22 100644
--- a/crates/router/tests/connectors/forte.rs
+++ b/crates/router/tests/connectors/forte.rs
@@ -157,6 +157,7 @@ async fn should_sync_authorized_payment() {
                 connector_meta: None,
                 mandate_id: None,
                 payment_method_type: None,
+                currency: enums::Currency::USD,
             }),
             get_default_payment_info(),
         )
diff --git a/crates/router/tests/connectors/nexinets.rs b/crates/router/tests/connectors/nexinets.rs
index 7f42048c981..5948855ae8d 100644
--- a/crates/router/tests/connectors/nexinets.rs
+++ b/crates/router/tests/connectors/nexinets.rs
@@ -125,6 +125,7 @@ async fn should_sync_authorized_payment() {
                 connector_meta,
                 mandate_id: None,
                 payment_method_type: None,
+                currency: enums::Currency::EUR,
             }),
             None,
         )
diff --git a/crates/router/tests/connectors/paypal.rs b/crates/router/tests/connectors/paypal.rs
index 14690ae2a73..bded135f45d 100644
--- a/crates/router/tests/connectors/paypal.rs
+++ b/crates/router/tests/connectors/paypal.rs
@@ -142,6 +142,7 @@ async fn should_sync_authorized_payment() {
                 sync_type: types::SyncRequestType::SinglePaymentSync,
                 connector_meta,
                 payment_method_type: None,
+                currency: enums::Currency::USD,
             }),
             get_default_payment_info(),
         )
@@ -339,6 +340,7 @@ async fn should_sync_auto_captured_payment() {
                 sync_type: types::SyncRequestType::SinglePaymentSync,
                 connector_meta,
                 payment_method_type: None,
+                currency: enums::Currency::USD,
             }),
             get_default_payment_info(),
         )
diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs
index 47ec71cc94e..6294108c820 100644
--- a/crates/router/tests/connectors/utils.rs
+++ b/crates/router/tests/connectors/utils.rs
@@ -997,6 +997,7 @@ impl Default for PaymentSyncType {
             sync_type: types::SyncRequestType::SinglePaymentSync,
             connector_meta: None,
             payment_method_type: None,
+            currency: enums::Currency::USD,
         };
         Self(data)
     }
diff --git a/crates/router/tests/connectors/zen.rs b/crates/router/tests/connectors/zen.rs
index c9a7be2b0a6..95017e7da0e 100644
--- a/crates/router/tests/connectors/zen.rs
+++ b/crates/router/tests/connectors/zen.rs
@@ -104,6 +104,7 @@ async fn should_sync_authorized_payment() {
                 connector_meta: None,
                 mandate_id: None,
                 payment_method_type: None,
+                currency: enums::Currency::USD,
             }),
             None,
         )
@@ -219,6 +220,7 @@ async fn should_sync_auto_captured_payment() {
                 connector_meta: None,
                 mandate_id: None,
                 payment_method_type: None,
+                currency: enums::Currency::USD,
             }),
             None,
         )

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

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

## Description
<!-- Describe your changes in detail -->
During settlement, payments processed through the Cryptopay connector may experience underpayment or overpayment, leading to fluctuations in the payment amount. The revised amount must be communicated through outgoing webhooks for the respective payment.

The new `price_amount` should be populated in `amount_captured` field.


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

## 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)?
-->
We need to check for the `amount_received` in outgoing webhook for underpaid/overpaid payments via cryptopay. The `amount_received` should be equal to the `invoice amount` on cryptopay dashboard for that payment.
In order to generate an underpaid/overpaid one must let the 10 min timer runout (on the redirection screen) and then only complete the payment.

Request:
```
curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_pjVrtRQmn0UDgtRtnzq6irFdNrbGHTgMvHFhX2RI15o84A5UFWvtlstAyB14V349' \
--data-raw '{
    "amount": 120,
    "currency": "USD",
    "confirm": true,
    "email": "guest@example.com",
    "return_url": "https://google.com",
    "payment_method": "crypto",
    "payment_method_type": "crypto_currency",
    "payment_experience": "redirect_to_url",
    "payment_method_data": {
        "crypto": {
            "pay_currency": "LTC"
        }
    }
}'
```

Response:
```
{
    "payment_id": "pay_Ay8enoELqeEY9qlS1tXk",
    "merchant_id": "merchant_1713942708",
    "status": "requires_customer_action",
    "amount": 120,
    "net_amount": 120,
    "amount_capturable": 120,
    "amount_received": 120,
    "connector": "cryptopay",
    "client_secret": "pay_Ay8enoELqeEY9qlS1tXk_secret_vTv6nv9uEHpNf7zSQgav",
    "created": "2024-04-26T07:13:48.793Z",
    "currency": "USD",
    "customer_id": null,
    "customer": null,
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": null,
    "payment_method": "crypto",
    "payment_method_data": {
        "crypto": {},
        "billing": null
    },
    "payment_token": null,
    "shipping": null,
    "billing": null,
    "order_details": null,
    "email": null,
    "name": null,
    "phone": null,
    "return_url": "https://google.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": null,
    "statement_descriptor_suffix": null,
    "next_action": {
        "type": "redirect_to_url",
        "redirect_to_url": "http://localhost:8080/payments/redirect/pay_Ay8enoELqeEY9qlS1tXk/merchant_1713942708/pay_Ay8enoELqeEY9qlS1tXk_1"
    },
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": "redirect_to_url",
    "payment_method_type": "crypto_currency",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": null,
    "manual_retry_allowed": null,
    "connector_transaction_id": "3c40efd2-2ade-4151-8760-aabb20a299fe",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "pay_Ay8enoELqeEY9qlS1tXk_1",
    "payment_link": null,
    "profile_id": "pro_HgGSGQyaRys8iaDsi7OX",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_z8isFAS0iNcgES4VN5Lm",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2024-04-26T07:28:48.793Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2024-04-26T07:13:49.156Z"
}
```

Outgoing Webhook:
![Screenshot 2024-04-26 at 12 23 33 PM](https://github.com/juspay/hyperswitch/assets/41580413/9f0f7f9d-f9c6-4718-a3ab-c0a4058b3995)

Cryptopay Dashboard:
![Screenshot 2024-04-26 at 12 29 36 PM](https://github.com/juspay/hyperswitch/assets/41580413/9a0be565-bc8e-4851-a070-4a45c344a9c1)


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