Bug: [FIX] : refund sync process scheduled time



refund sync process scheduled time
diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs
index ed44f61b4da..3d8463c2554 100644
--- a/crates/router/src/core/refunds.rs
+++ b/crates/router/src/core/refunds.rs
@@ -4,7 +4,7 @@ use std::collections::HashMap;
 #[cfg(feature = "olap")]
 use api_models::admin::MerchantConnectorInfo;
 use common_utils::{
-    ext_traits::AsyncExt,
+    ext_traits::{AsyncExt, StringExt},
     types::{ConnectorTransactionId, MinorUnit},
 };
 use diesel_models::{process_tracker::business_status, refund as diesel_refund};
@@ -14,7 +14,9 @@ use hyperswitch_domain_models::{
 };
 use hyperswitch_interfaces::integrity::{CheckIntegrity, FlowIntegrity, GetIntegrityObject};
 use router_env::{instrument, tracing};
-use scheduler::{consumer::types::process_data, utils as process_tracker_utils};
+use scheduler::{
+    consumer::types::process_data, errors as sch_errors, utils as process_tracker_utils,
+};
 #[cfg(feature = "olap")]
 use strum::IntoEnumIterator;
 
@@ -40,7 +42,6 @@ use crate::{
         transformers::{ForeignFrom, ForeignInto},
     },
     utils::{self, OptionExt},
-    workflows::payment_sync,
 };
 
 // ********************************************** REFUND EXECUTE **********************************************
@@ -1439,7 +1440,7 @@ pub async fn sync_refund_with_gateway_workflow(
                 .await?
         }
         _ => {
-            _ = payment_sync::retry_sync_task(
+            _ = retry_refund_sync_task(
                 &*state.store,
                 response.connector,
                 response.merchant_id,
@@ -1452,6 +1453,33 @@ pub async fn sync_refund_with_gateway_workflow(
     Ok(())
 }
 
+/// Schedule the task for refund retry
+///
+/// Returns bool which indicates whether this was the last refund retry or not
+pub async fn retry_refund_sync_task(
+    db: &dyn db::StorageInterface,
+    connector: String,
+    merchant_id: common_utils::id_type::MerchantId,
+    pt: storage::ProcessTracker,
+) -> Result<bool, sch_errors::ProcessTrackerError> {
+    let schedule_time =
+        get_refund_sync_process_schedule_time(db, &connector, &merchant_id, pt.retry_count + 1)
+            .await?;
+
+    match schedule_time {
+        Some(s_time) => {
+            db.as_scheduler().retry_process(pt, s_time).await?;
+            Ok(false)
+        }
+        None => {
+            db.as_scheduler()
+                .finish_process_with_business_status(pt, business_status::RETRIES_EXCEEDED)
+                .await?;
+            Ok(true)
+        }
+    }
+}
+
 #[instrument(skip_all)]
 pub async fn start_refund_workflow(
     state: &SessionState,
@@ -1614,7 +1642,12 @@ pub async fn add_refund_sync_task(
 ) -> RouterResult<storage::ProcessTracker> {
     let task = "SYNC_REFUND";
     let process_tracker_id = format!("{runner}_{task}_{}", refund.internal_reference_id);
-    let schedule_time = common_utils::date_time::now();
+    let schedule_time =
+        get_refund_sync_process_schedule_time(db, &refund.connector, &refund.merchant_id, 0)
+            .await
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Failed to fetch schedule time for refund sync process")?
+            .unwrap_or_else(common_utils::date_time::now);
     let refund_workflow_tracking_data = refund_to_refund_core_workflow_model(refund);
     let tag = ["REFUND"];
     let process_tracker_entry = storage::ProcessTrackerNew::new(
@@ -1688,15 +1721,20 @@ pub async fn get_refund_sync_process_schedule_time(
     merchant_id: &common_utils::id_type::MerchantId,
     retry_count: i32,
 ) -> Result<Option<time::PrimitiveDateTime>, errors::ProcessTrackerError> {
-    let redis_mapping: errors::CustomResult<process_data::ConnectorPTMapping, errors::RedisError> =
-        db::get_and_deserialize_key(
-            db,
-            &format!("pt_mapping_refund_sync_{connector}"),
-            "ConnectorPTMapping",
-        )
-        .await;
-
-    let mapping = match redis_mapping {
+    let mapping: common_utils::errors::CustomResult<
+        process_data::ConnectorPTMapping,
+        errors::StorageError,
+    > = db
+        .find_config_by_key(&format!("pt_mapping_refund_sync_{connector}"))
+        .await
+        .map(|value| value.config)
+        .and_then(|config| {
+            config
+                .parse_struct("ConnectorPTMapping")
+                .change_context(errors::StorageError::DeserializationFailed)
+        });
+
+    let mapping = match mapping {
         Ok(x) => x,
         Err(err) => {
             logger::error!("Error: while getting connector mapping: {err:?}");
@@ -1704,8 +1742,7 @@ pub async fn get_refund_sync_process_schedule_time(
         }
     };
 
-    let time_delta =
-        process_tracker_utils::get_schedule_time(mapping, merchant_id, retry_count + 1);
+    let time_delta = process_tracker_utils::get_schedule_time(mapping, merchant_id, retry_count);
 
     Ok(process_tracker_utils::get_time_from_delta(time_delta))
 }

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

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

## Description

Before refund sync process tracker task was using pt_mapping_{connector} for sync retries (this is config name of Psync which was incorrect) 

Fixes made in this PR - 
- We are using pt_mapping_refund_sync_{connector}
- refund sync which was using retry_sync_task so made retry_refund_sync_task which will handle the refund task
- fix refund sync workflow being incorrectly scheduled for 2nd retry attempt instead of 1st retry attempt
- ConnectorPTMapping should also be read from db , previously we were always reading only from redis
 
### 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?
Create a process tracker config for Adyen connector
```
curl --location 'http://localhost:8080/configs/pt_mapping_refund_sync_adyen' \
--header 'Content-Type: application/json' \
--header 'api-key: _____' \
--data '{
    "key": "pt_mapping_refund_sync_adyen",
    "value": "{\"custom_merchant_mapping\":{},\"default_mapping\":{\"start_after\":180,\"frequencies\":[[20,2],[30,2],[40,2],[60,2],[2400,1],[43200,4],[86400,3],[345600,2]]},\"max_retries_count\":18}"
}'
```
Response
```
{
    "key": "pt_mapping_refund_sync_adyen",
    "value": "{\"custom_merchant_mapping\":{},\"default_mapping\":{\"start_after\":180,\"frequencies\":[[20,2],[30,2],[40,2],[60,2],[2400,1],[43200,4],[86400,3],[345600,2]]},\"max_retries_count\":18}"
}
```

Create a no 3ds card payment via Adyen
```
{
    "amount": 1000,
    "currency": "USD",
    "confirm": true,
    "capture_method": "automatic",
    "capture_on": "2022-09-10T10:11:12Z",
    "amount_to_capture": 1000,
    "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",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number": "4111111111111111",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "card_cvc": "737"
        }
        
    },
    "metadata": {
        "order_id": "ORD-12345",
        "sdata": {
            "customer_segment": "premium",
            "purchase_platform": "web_store"
        }
    },
    "routing": {
        "type": "single",
        "data": "adyen"
    },
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "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",
    "merchant_order_reference_id": "order_narvar12",
    "browser_info": {
        "os_type": "macOS",
        "language": "en-GB",
        "time_zone": -330,
        "ip_address": "103.159.11.202",
        "os_version": "10.15.7",
        "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
        "color_depth": 24,
        "device_model": "Macintosh",
        "java_enabled": true,
        "screen_width": 1920,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 1080,
        "accept_language": "en",
        "java_script_enabled": true
    },
    "profile_id": "{{payment_profile_id}}"
}
```
Response
```
{
    "payment_id": "pay_PuKa9OvV1e3yrcdZcfEo",
    "merchant_id": "merchant_1755523756",
    "status": "succeeded",
    "amount": 1000,
    "net_amount": 1000,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 1000,
    "connector": "adyen",
    "client_secret": "pay_PuKa9OvV1e3yrcdZcfEo_secret_V1Yu4MdVf3iEhwKygr0H",
    "created": "2025-08-19T06:21:08.206Z",
    "currency": "USD",
    "customer_id": "StripeCustomer",
    "customer": {
        "id": "StripeCustomer",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": "Its my first payment request",
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "1111",
            "card_type": "CREDIT",
            "card_network": "Visa",
            "card_issuer": "JP Morgan",
            "card_issuing_country": "INDIA",
            "card_isin": "411111",
            "card_extended_bin": null,
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null,
            "origin_zip": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null,
            "origin_zip": null
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": "https://duck.com/",
    "authentication_type": "no_three_ds",
    "statement_descriptor_name": "joseph",
    "statement_descriptor_suffix": "JS",
    "next_action": null,
    "cancellation_reason": null,
    "error_code": null,
    "error_message": null,
    "unified_code": null,
    "unified_message": null,
    "payment_experience": null,
    "payment_method_type": "credit",
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "StripeCustomer",
        "created_at": 1755584468,
        "expires": 1755588068,
        "secret": "epk_eadaf298af8f4d58b46ac00860f9e440"
    },
    "manual_retry_allowed": false,
    "connector_transaction_id": "HH2TDB3NBZXRHBV5",
    "frm_message": null,
    "metadata": {
        "sdata": {
            "customer_segment": "premium",
            "purchase_platform": "web_store"
        },
        "order_id": "ORD-12345"
    },
    "connector_metadata": null,
    "feature_metadata": {
        "redirect_response": null,
        "search_tags": null,
        "apple_pay_recurring_details": null,
        "gateway_system": "direct"
    },
    "reference_id": "pay_PuKa9OvV1e3yrcdZcfEo_1",
    "payment_link": null,
    "profile_id": "pro_IBcsy1G8u4AqtpDlpoQ6",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_ST0rQKrUA8r95Bvci4FV",
    "incremental_authorization_allowed": false,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-08-19T06:36:08.206Z",
    "fingerprint": null,
    "browser_info": {
        "os_type": "macOS",
        "language": "en-GB",
        "time_zone": -330,
        "ip_address": "103.159.11.202",
        "os_version": "10.15.7",
        "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36",
        "color_depth": 24,
        "device_model": "Macintosh",
        "java_enabled": true,
        "screen_width": 1920,
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "screen_height": 1080,
        "accept_language": "en",
        "java_script_enabled": true
    },
    "payment_channel": null,
    "payment_method_id": null,
    "network_transaction_id": null,
    "payment_method_status": null,
    "updated": "2025-08-19T06:21:11.290Z",
    "split_payments": null,
    "frm_metadata": null,
    "extended_authorization_applied": null,
    "capture_before": null,
    "merchant_order_reference_id": "order_narvar12",
    "order_tax_amount": null,
    "connector_mandate_id": null,
    "card_discovery": "manual",
    "force_3ds_challenge": false,
    "force_3ds_challenge_trigger": false,
    "issuer_error_code": null,
    "issuer_error_message": null,
    "is_iframe_redirection_enabled": null,
    "whole_connector_response": null,
    "enable_partial_authorization": null
}
```

Create a Refund Request
```
{
    "payment_id": "{{payment_id}}",
    "amount": 600,
    "reason": "Customer returned product",
    "refund_type": "instant",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}
```
Response
```
{
    "refund_id": "ref_ItP2mAScOD5yeWYmsgWA",
    "payment_id": "pay_PuKa9OvV1e3yrcdZcfEo",
    "amount": 600,
    "currency": "USD",
    "status": "pending",
    "reason": "Customer returned product",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    },
    "error_message": null,
    "error_code": null,
    "unified_code": null,
    "unified_message": null,
    "created_at": "2025-08-19T06:21:25.378Z",
    "updated_at": "2025-08-19T06:21:25.738Z",
    "connector": "adyen",
    "profile_id": "pro_IBcsy1G8u4AqtpDlpoQ6",
    "merchant_connector_id": "mca_ST0rQKrUA8r95Bvci4FV",
    "split_refunds": null,
    "issuer_error_code": null,
    "issuer_error_message": null
}
```
In db search for this refund id 
`select * from refund where refund_id = 'ref_ItP2mAScOD5yeWYmsgWA';`
We will get  internal_reference_id      | refid_DFtahM9L64gRSkyzkibF
<img width="964" height="715" alt="Screenshot 2025-08-19 at 11 57 31 AM" src="https://github.com/user-attachments/assets/c237e56e-f6a2-4ad0-b82c-e462f3f90017" />


Using this internal_reference_id we will check in process_tracker table to check the scheduled time
`select * from process_tracker where id like '%refid_DFtahM9L64gRSkyzkibF%';`
We can scheduled time is 3min after updated_at as in config start_after time was set 180 sec
<img width="1124" height="349" alt="Screenshot 2025-08-19 at 11 57 58 AM" src="https://github.com/user-attachments/assets/59250474-a27c-4815-8598-ca8ade392121" />

After 3 min if we check process_tracker 
scheduled time is 20sec min after updated_at as in config 1st frequency was set 20 sec 
<img width="1252" height="344" alt="Screenshot 2025-08-19 at 12 01 19 PM" src="https://github.com/user-attachments/assets/650b5b2f-f16b-4eae-a8ab-9ddc513925a0" />


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