Bug: fix(recovery) : Populate connector request reference id in revenue recovery record attempt flow.



Previously there was no restriction in confirmData to connector request reference id to be mandatory. Recovery internally uses proxy flow, which internally uses confirmData where connector request reference id was hardcoded to none.
diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
index c35fbc3163e..86821bfe6ef 100644
--- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
+++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs
@@ -646,7 +646,6 @@ impl PaymentAttempt {
         let payment_method_type_data = payment_intent.get_payment_method_type();
 
         let payment_method_subtype_data = payment_intent.get_payment_method_sub_type();
-
         let authentication_type = payment_intent.authentication_type.unwrap_or_default();
         Ok(Self {
             payment_id: payment_intent.id.clone(),
@@ -752,7 +751,10 @@ impl PaymentAttempt {
             .as_ref()
             .map(|txn_id| txn_id.get_id().clone());
         let connector = request.connector.map(|connector| connector.to_string());
-
+        let connector_request_reference_id = payment_intent
+            .merchant_reference_id
+            .as_ref()
+            .map(|id| id.get_string_repr().to_owned());
         Ok(Self {
             payment_id: payment_intent.id.clone(),
             merchant_id: payment_intent.merchant_id.clone(),
@@ -804,7 +806,7 @@ impl PaymentAttempt {
             charges: None,
             processor_merchant_id: payment_intent.merchant_id.clone(),
             created_by: None,
-            connector_request_reference_id: None,
+            connector_request_reference_id,
         })
     }
 
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index 59de54f41fa..2f70a79ec87 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -4042,6 +4042,16 @@ where
             )
             .await?,
         ));
+    operation
+        .to_domain()?
+        .populate_payment_data(
+            state,
+            payment_data,
+            merchant_context,
+            business_profile,
+            &connector,
+        )
+        .await?;
 
     let mut router_data = payment_data
         .construct_router_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 838cbf9dedf..a65f4cdd99e 100644
--- a/crates/router/src/core/payments/operations/proxy_payments_intent.rs
+++ b/crates/router/src/core/payments/operations/proxy_payments_intent.rs
@@ -6,6 +6,7 @@ use error_stack::ResultExt;
 use hyperswitch_domain_models::{
     payment_method_data::PaymentMethodData, payments::PaymentConfirmData,
 };
+use hyperswitch_interfaces::api::ConnectorSpecifications;
 use masking::PeekInterface;
 use router_env::{instrument, tracing};
 
@@ -15,7 +16,7 @@ use crate::{
         errors::{self, CustomResult, RouterResult, StorageErrorExt},
         payments::{
             operations::{self, ValidateStatusForOperation},
-            OperationSessionGetters,
+            OperationSessionGetters, OperationSessionSetters,
         },
     },
     routes::{app::ReqState, SessionState},
@@ -303,6 +304,24 @@ impl<F: Clone + Send + Sync> Domain<F, ProxyPaymentsRequest, PaymentConfirmData<
     )> {
         Ok((Box::new(self), None, None))
     }
+    #[instrument(skip_all)]
+    async fn populate_payment_data<'a>(
+        &'a self,
+        _state: &SessionState,
+        payment_data: &mut PaymentConfirmData<F>,
+        _merchant_context: &domain::MerchantContext,
+        _business_profile: &domain::Profile,
+        connector_data: &api::ConnectorData,
+    ) -> CustomResult<(), errors::ApiErrorResponse> {
+        let connector_request_reference_id = connector_data
+            .connector
+            .generate_connector_request_reference_id(
+                &payment_data.payment_intent,
+                &payment_data.payment_attempt,
+            );
+        payment_data.set_connector_request_reference_id(Some(connector_request_reference_id));
+        Ok(())
+    }
 
     async fn perform_routing<'a>(
         &'a self,
diff --git a/crates/router/src/routes/payments.rs b/crates/router/src/routes/payments.rs
index 020a1fbf713..e2363633dad 100644
--- a/crates/router/src/routes/payments.rs
+++ b/crates/router/src/routes/payments.rs
@@ -232,7 +232,7 @@ pub async fn payments_get_intent(
                 header_payload.clone(),
             )
         },
-        auth::api_or_client_auth(
+        auth::api_or_client_or_jwt_auth(
             &auth::V2ApiKeyAuth {
                 is_connected_allowed: false,
                 is_platform_allowed: false,
@@ -240,6 +240,9 @@ pub async fn payments_get_intent(
             &auth::V2ClientAuth(common_utils::types::authentication::ResourceId::Payment(
                 global_payment_id.clone(),
             )),
+            &auth::JWTAuth {
+                permission: Permission::ProfileRevenueRecoveryRead,
+            },
             req.headers(),
         ),
         api_locking::LockAction::NotApplicable,
diff --git a/crates/router/src/services/authentication.rs b/crates/router/src/services/authentication.rs
index 41b7bd3d87b..d16063939a1 100644
--- a/crates/router/src/services/authentication.rs
+++ b/crates/router/src/services/authentication.rs
@@ -2698,7 +2698,27 @@ where
         api_auth
     }
 }
-
+#[cfg(feature = "v2")]
+pub fn api_or_client_or_jwt_auth<'a, T, A>(
+    api_auth: &'a dyn AuthenticateAndFetch<T, A>,
+    client_auth: &'a dyn AuthenticateAndFetch<T, A>,
+    jwt_auth: &'a dyn AuthenticateAndFetch<T, A>,
+    headers: &HeaderMap,
+) -> &'a dyn AuthenticateAndFetch<T, A>
+where
+{
+    if let Ok(val) = HeaderMapStruct::new(headers).get_auth_string_from_header() {
+        if val.trim().starts_with("api-key=") {
+            api_auth
+        } else if is_jwt_auth(headers) {
+            jwt_auth
+        } else {
+            client_auth
+        }
+    } else {
+        api_auth
+    }
+}
 #[derive(Debug)]
 pub struct PublishableKeyAuth;
 

## 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 -->
Previously there was no restriction in confirmData to connector request reference id to be mandatory. Recovery internally uses proxy flow, which internally uses confirmData where connector request reference id was hardcoded to none. This Pr adds supports to generate connector request reference to merchant reference id. 

This Pr also has changes of adding support for jwt auth to payment get for dashboard use case. Previously payment sync was used in dashboard to retrieve payment details, but this would fail now in v2 because if there are no attempts created payment get would return 4xx since active attempt is mandotory. To solve this now dashboard is relying on payment get and payment get attempt list api's .

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


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

Test case 1 : 
**step 1 :** create a profile in hyperswitch and wait for 8 mins to move it from monitering to cascading , this would only get updated when webhooks are triggered .
**step 2 :** create payment mca
```
curl --location 'http://localhost:8080/v2/connector-accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-merchant-id: test_seller_AGZCJiHwUNV1seyoepOE' \
--header 'x-profile-id: pro_kYmv4lQjIZFbAqoOVY4M' \
--header 'Authorization: admin-api-key=test_admin' \
--data '{
  "connector_type": "payment_processor",
  "connector_name": "stripe",
  "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "stripe-api-key"
    },
  "profile_id": "pro_kYmv4lQjIZFbAqoOVY4M"
}'
``` 
**step 4 : ** configure stripe billing connector. 
```
curl --location 'http://localhost:8080/v2/connector-accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-merchant-id: test_seller_AGZCJiHwUNV1seyoepOE' \
--header 'x-profile-id: pro_kYmv4lQjIZFbAqoOVY4M' \
--header 'Authorization: admin-api-key=test_admin' \
--data '{
    "connector_type": "billing_processor",
    "connector_name": "stripebilling",
    "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "{{stripe-api-key}}"
    },
    "connector_webhook_details": {
        "merchant_secret": "{{stripe-webhook-key}}",
        "additional_secret": "password"
    },
    "feature_metadata": {
        "revenue_recovery": {
            "max_retry_count": 7,
            "billing_connector_retry_threshold": 2,
            "billing_account_reference": {
                "{{payment_connector_mca_created_in_last_step}}": "stripebilling"
            }
        }
    },
    "profile_id": "pro_kYmv4lQjIZFbAqoOVY4M"
}'
```
step 5 : Trigger failed invoice from stripe billing by creating new subscription and move the test clock to 1 week.
This would trigger few external payments. once it exhausts retry threshold it will create process tracker entry. Follwed by 2 mins it would create all retries by hyperswitch as shown below.

<img width="935" alt="Screenshot 2025-06-23 at 7 00 57 PM" src="https://github.com/user-attachments/assets/d51d3d9e-82ff-4385-b318-dbb37230cee8" />


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