diff --git a/apps/upi_core/lib/upi_core/adapters/npci_adapter.ex b/apps/upi_core/lib/upi_core/adapters/npci_adapter.ex
index aeec55e..902845a 100644
--- a/apps/upi_core/lib/upi_core/adapters/npci_adapter.ex
+++ b/apps/upi_core/lib/upi_core/adapters/npci_adapter.ex
@@ -31,16 +31,19 @@ defmodule UpiCore.Adapters.NpciAdapter do
   """
   @spec generate_reqpay_ack(String.t(), String.t()) :: String.t()
   def generate_reqpay_ack(msg_id, org_txn_id) do
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/"
-             api="ReqPay"
-             reqMsgId="#{msg_id}"
-             orgTxnId="#{org_txn_id}"
-             ts="#{timestamp}"/>
-    """
+    # [ACK ReqPay ts / orgTxnId / txnId] CHANGED: standard Ack with IST ts; the UTC ts (microseconds) and the extra orgTxnId / txnId attribute and XML prolog are gone
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <?xml version="1.0" encoding="UTF-8"?>
+    # OLD:     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/"
+    # OLD:              api="ReqPay"
+    # OLD:              reqMsgId="#{msg_id}"
+    # OLD:              orgTxnId="#{org_txn_id}"
+    # OLD:              ts="#{timestamp}"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqPay", msg_id)
 
     Logger.info("[NPCI ACK] Generated ReqPay ACK for msgId=#{msg_id}, orgTxnId=#{org_txn_id}")
     Logger.debug("[NPCI ACK] ACK XML: #{ack_xml}")
@@ -53,16 +56,19 @@ defmodule UpiCore.Adapters.NpciAdapter do
   """
   @spec generate_reqvalqr_ack(String.t(), String.t()) :: String.t()
   def generate_reqvalqr_ack(msg_id, txn_id) do
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/"
-             api="ReqValQr"
-             reqMsgId="#{msg_id}"
-             txnId="#{txn_id}"
-             ts="#{timestamp}"/>
-    """
+    # [ACK ReqValQr ts / orgTxnId / txnId] CHANGED: standard Ack with IST ts; the UTC ts (microseconds) and the extra orgTxnId / txnId attribute and XML prolog are gone
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <?xml version="1.0" encoding="UTF-8"?>
+    # OLD:     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/"
+    # OLD:              api="ReqValQr"
+    # OLD:              reqMsgId="#{msg_id}"
+    # OLD:              txnId="#{txn_id}"
+    # OLD:              ts="#{timestamp}"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqValQr", msg_id)
 
     Logger.info("[NPCI ACK] Generated ReqValQr ACK for msgId=#{msg_id}, txnId=#{txn_id}")
     Logger.debug("[NPCI ACK] ACK XML: #{ack_xml}")
@@ -75,16 +81,19 @@ defmodule UpiCore.Adapters.NpciAdapter do
   """
   @spec generate_reqchktxn_ack(String.t(), String.t()) :: String.t()
   def generate_reqchktxn_ack(msg_id, org_txn_id) do
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/"
-             api="ReqChkTxn"
-             reqMsgId="#{msg_id}"
-             orgTxnId="#{org_txn_id}"
-             ts="#{timestamp}"/>
-    """
+    # [ACK ReqChkTxn ts / orgTxnId / txnId] CHANGED: standard Ack with IST ts; the UTC ts (microseconds) and the extra orgTxnId / txnId attribute and XML prolog are gone
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <?xml version="1.0" encoding="UTF-8"?>
+    # OLD:     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/"
+    # OLD:              api="ReqChkTxn"
+    # OLD:              reqMsgId="#{msg_id}"
+    # OLD:              orgTxnId="#{org_txn_id}"
+    # OLD:              ts="#{timestamp}"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqChkTxn", msg_id)
 
     Logger.info("[NPCI ACK] Generated ReqChkTxn ACK for msgId=#{msg_id}, orgTxnId=#{org_txn_id}")
     Logger.debug("[NPCI ACK] ACK XML: #{ack_xml}")
diff --git a/apps/upi_core/lib/upi_core/npci_format.ex b/apps/upi_core/lib/upi_core/npci_format.ex
index 627fd51..d10eb67 100644
--- a/apps/upi_core/lib/upi_core/npci_format.ex
+++ b/apps/upi_core/lib/upi_core/npci_format.ex
@@ -133,4 +133,17 @@ defmodule UpiCore.NpciFormat do
     |> String.replace("\"", "&quot;")
     |> String.replace("'", "&apos;")
   end
+
+  @doc """
+  The standard NPCI Ack we send for ReqValQr / ReqPay / ReqHbt / ReqChkTxn:
+
+      <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<api>" reqMsgId="<reqMsgId>" ts="<IST time>"/>
+
+  `req_msg_id` is the msgId of the request being acknowledged, exactly as received; `ts` is `ist_now/0`.
+  Nothing else (no orgTxnId / txnId / Head) is part of the Ack.
+  """
+  @spec ack_xml(String.t(), String.t() | nil) :: String.t()
+  def ack_xml(api, req_msg_id) do
+    ~s(<ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="#{xml_attr(api)}" reqMsgId="#{xml_attr(req_msg_id)}" ts="#{ist_now()}"/>)
+  end
 end
diff --git a/apps/upi_core/lib/upi_core/special_handlers/acc_ip_05_scenario.ex b/apps/upi_core/lib/upi_core/special_handlers/acc_ip_05_scenario.ex
index aeee0e2..767c965 100644
--- a/apps/upi_core/lib/upi_core/special_handlers/acc_ip_05_scenario.ex
+++ b/apps/upi_core/lib/upi_core/special_handlers/acc_ip_05_scenario.ex
@@ -63,11 +63,14 @@ defmodule UpiCore.SpecialHandlers.AccIp05Scenario do
 
     # Generate ACK XML
     msg_id = get_msg_id_from_parsed_data(parsed_data)
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" reqMsgId="#{msg_id}" ts="#{timestamp}"/>
-    """
+    # [ACK ACC_IP_05 ReqPay ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" reqMsgId="#{msg_id}" ts="#{timestamp}"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqPay", msg_id)
 
     Logger.info("🎯 ACCIP05: ACK generated successfully for msgId=#{msg_id}")
     Logger.debug("🎯 ACCIP05: ACK XML: #{ack_xml}")
diff --git a/apps/upi_core/lib/upi_core/special_handlers/acc_ip_08_scenario.ex b/apps/upi_core/lib/upi_core/special_handlers/acc_ip_08_scenario.ex
index 5962531..465a48c 100644
--- a/apps/upi_core/lib/upi_core/special_handlers/acc_ip_08_scenario.ex
+++ b/apps/upi_core/lib/upi_core/special_handlers/acc_ip_08_scenario.ex
@@ -204,15 +204,18 @@ defmodule UpiCore.SpecialHandlers.AccIp08Scenario do
 
   @doc false
   def generate_ack_response(xml_body, parsed_data) do
-    msg_id = Map.get(parsed_data, :msg_id) || generate_msg_id()
-
-    """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <Ack xmlns="http://npci.org/upi/schema/">
-      <Head ver="2.0" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" orgId="MERCURY001" msgId="#{msg_id}"/>
-      <Resp result="OK" code="00" desc="ACK"/>
-    </Ack>
-    """
+    # [ACK ACC_IP_08 ReqChkTxn Ack] CHANGED: the fake <Ack><Head>...<Resp result="OK"> document (orgId MERCURY001, UTC ts, generated msgId when missing) is replaced by the standard Ack of the request msgId
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     msg_id = Map.get(parsed_data, :msg_id) || generate_msg_id()
+    # OLD:
+    # OLD:     """
+    # OLD:     <?xml version="1.0" encoding="UTF-8"?>
+    # OLD:     <Ack xmlns="http://npci.org/upi/schema/">
+    # OLD:       <Head ver="2.0" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" orgId="MERCURY001" msgId="#{msg_id}"/>
+    # OLD:       <Resp result="OK" code="00" desc="ACK"/>
+    # OLD:     </Ack>
+    # OLD:     """
+    UpiCore.NpciFormat.ack_xml("ReqChkTxn", Map.get(parsed_data, :msg_id))
   end
 
   defp generate_and_send_x7_resp_chk_txn(xml_body, parsed_data) do
diff --git a/apps/upi_core/lib/upi_core/transactions/npci_heartbeat_service.ex b/apps/upi_core/lib/upi_core/transactions/npci_heartbeat_service.ex
index 9e2b4c7..854dc03 100644
--- a/apps/upi_core/lib/upi_core/transactions/npci_heartbeat_service.ex
+++ b/apps/upi_core/lib/upi_core/transactions/npci_heartbeat_service.ex
@@ -192,8 +192,10 @@ defmodule UpiCore.Transactions.NpciHeartbeatService do
 
     ack_data = %{
       api: "RespHbt",
-      req_msg_id: parsed_response[:msg_id],
-      timestamp: DateTime.utc_now() |> DateTime.to_iso8601()
+      # [ACK RespHbt Ack ts] CHANGED: no UTC timestamp passed; generate_ack_response writes the IST ts
+      # OLD:       req_msg_id: parsed_response[:msg_id],
+      # OLD:       timestamp: DateTime.utc_now() |> DateTime.to_iso8601()
+      req_msg_id: parsed_response[:msg_id]
     }
 
     case UpiXmlSchema.generate_ack_response(ack_data) do
diff --git a/apps/upi_core/lib/upi_core/upi_xml_schema.ex b/apps/upi_core/lib/upi_core/upi_xml_schema.ex
index 108f86b..e27b64e 100644
--- a/apps/upi_core/lib/upi_core/upi_xml_schema.ex
+++ b/apps/upi_core/lib/upi_core/upi_xml_schema.ex
@@ -1645,14 +1645,17 @@ end
   <ns2:Ack api="ReqHbt" reqMsgId="PAM3eaf410eff2349638897034ef263d1e3" ts="2024-01-29T07:43:56+00:00" xmlns:ns2="http://npci.org/upi/schema/"/>
   """
   def generate_resp_hbt(response_data) do
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    xml = """
-    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-    <ns2:Ack api="ReqHbt" reqMsgId="#{response_data.req_msg_id}" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
-    """
-
-    {:ok, String.trim(xml)}
+    # [ACK ReqHbt ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     xml = """
+    # OLD:     <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+    # OLD:     <ns2:Ack api="ReqHbt" reqMsgId="#{response_data.req_msg_id}" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
+    # OLD:     """
+    # OLD:
+    # OLD:     {:ok, String.trim(xml)}
+    {:ok, NpciFormat.ack_xml("ReqHbt", response_data.req_msg_id)}
   end
 
   @doc """
@@ -1864,12 +1867,15 @@ end
     require Logger
     Logger.info("Generating ACK response for API: #{ack_data.api}")
 
-    timestamp = ack_data[:timestamp] || (DateTime.utc_now() |> DateTime.to_iso8601())
-
-    xml = """
-    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-    <ns2:Ack api="#{ack_data.api}" reqMsgId="#{ack_data.req_msg_id}" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
-    """
+    # [ACK generic Ack ts] CHANGED: IST ts via NpciFormat (a caller-supplied UTC timestamp is no longer used)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = ack_data[:timestamp] || (DateTime.utc_now() |> DateTime.to_iso8601())
+    # OLD:
+    # OLD:     xml = """
+    # OLD:     <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+    # OLD:     <ns2:Ack api="#{ack_data.api}" reqMsgId="#{ack_data.req_msg_id}" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
+    # OLD:     """
+    xml = NpciFormat.ack_xml(ack_data.api, ack_data.req_msg_id)
 
     Logger.info("✅ ACK XML generated for API: #{ack_data.api}")
     {:ok, String.trim(xml)}
diff --git a/apps/upi_core/test/upi_core/ack_test.exs b/apps/upi_core/test/upi_core/ack_test.exs
new file mode 100644
index 0000000..427b614
--- /dev/null
+++ b/apps/upi_core/test/upi_core/ack_test.exs
@@ -0,0 +1,59 @@
+defmodule UpiCore.AckTest do
+  use ExUnit.Case, async: true
+
+  import SweetXml
+
+  alias UpiCore.Adapters.NpciAdapter
+  alias UpiCore.UpiXmlSchema
+  alias UpiCore.SpecialHandlers.{AccIp05Scenario, AccIp08Scenario}
+
+  @msg_id "NPCIREQMSG00000000000000000000001"
+  @ts_re ~r/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+05:30$/
+
+  defp attr(xml, path), do: xpath(xml, ~x"#{path}"s)
+
+  defp assert_standard_ack(xml, api) do
+    assert String.trim(xml) =~ ~r|^<ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="#{api}" reqMsgId="#{@msg_id}" ts="[^"]+"/>$|
+    assert attr(xml, "/*[local-name()='Ack']/@api") == api
+    assert attr(xml, "/*[local-name()='Ack']/@reqMsgId") == @msg_id
+    assert attr(xml, "/*[local-name()='Ack']/@ts") =~ @ts_re
+    assert attr(xml, "/*[local-name()='Ack']/@orgTxnId") == ""
+    assert attr(xml, "/*[local-name()='Ack']/@txnId") == ""
+    assert attr(xml, "count(//*[local-name()='Head'])") == "0"
+  end
+
+  test "NpciAdapter ReqPay / ReqValQr / ReqChkTxn Acks" do
+    assert_standard_ack(NpciAdapter.generate_reqpay_ack(@msg_id, "ORGTXN1"), "ReqPay")
+    assert_standard_ack(NpciAdapter.generate_reqvalqr_ack(@msg_id, "TXN1"), "ReqValQr")
+    assert_standard_ack(NpciAdapter.generate_reqchktxn_ack(@msg_id, "ORGTXN1"), "ReqChkTxn")
+  end
+
+  test "ReqHbt Ack (generate_resp_hbt)" do
+    {:ok, xml} = UpiXmlSchema.generate_resp_hbt(%{req_msg_id: @msg_id, org_id: "MER101", msg_id: "ignored"})
+    assert_standard_ack(xml, "ReqHbt")
+  end
+
+  test "generic Ack (generate_ack_response) ignores a caller supplied UTC timestamp" do
+    {:ok, xml} = UpiXmlSchema.generate_ack_response(%{api: "RespHbt", req_msg_id: @msg_id, timestamp: "2026-01-01T00:00:00.123456Z"})
+    assert_standard_ack(xml, "RespHbt")
+    refute xml =~ "Z\""
+  end
+
+  test "ACC_IP_05 immediate ReqPay Ack" do
+    {:ok, xml} = AccIp05Scenario.handle_req_pay_immediate_ack("<ReqPay/>", %{msg_id: @msg_id})
+    assert_standard_ack(xml, "ReqPay")
+  end
+
+  test "ACC_IP_08 ReqChkTxn Ack is the standard Ack, not the fake <Ack><Head> document" do
+    xml = AccIp08Scenario.generate_ack_response("<ReqChkTxn/>", %{msg_id: @msg_id})
+    assert_standard_ack(xml, "ReqChkTxn")
+    refute xml =~ "MERCURY001"
+    refute xml =~ ~s(result="OK")
+  end
+
+  test "ReqPay Ack of generate_ack_reqpay_response is unchanged and IST" do
+    xml = UpiXmlSchema.generate_ack_reqpay_response(@msg_id)
+    assert attr(xml, "/*[local-name()='Ack']/@api") == "ReqPay"
+    assert attr(xml, "/*[local-name()='Ack']/@ts") =~ @ts_re
+  end
+end
diff --git a/apps/upi_core/test/upi_core/npci_format_test.exs b/apps/upi_core/test/upi_core/npci_format_test.exs
index 4ed0301..5fab4c5 100644
--- a/apps/upi_core/test/upi_core/npci_format_test.exs
+++ b/apps/upi_core/test/upi_core/npci_format_test.exs
@@ -79,6 +79,23 @@ defmodule UpiCore.NpciFormatTest do
     end
   end
 
+  describe "ack_xml/2" do
+    test "is exactly the standard NPCI Ack with an IST ts" do
+      xml = NpciFormat.ack_xml("ReqValQr", "NPCIMSG0000000000000000000000000001")
+
+      assert xml =~
+               ~r|^<ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqValQr" reqMsgId="NPCIMSG0000000000000000000000000001" ts="\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+05:30"/>$|
+    end
+
+    test "has no orgTxnId / txnId / Head / prolog and escapes the msgId" do
+      xml = NpciFormat.ack_xml("ReqPay", ~s(a"b&c))
+      refute xml =~ "orgTxnId"
+      refute xml =~ "<Head"
+      refute xml =~ "<?xml"
+      assert xml =~ ~s(reqMsgId="a&quot;b&amp;c")
+    end
+  end
+
   describe "format_amount/1" do
     test "pads to 2 decimals" do
       assert NpciFormat.format_amount("150.0") == "150.00"
diff --git a/apps/upi_dynamic/lib/upi_dynamic/controllers/api/v1/upi_controller.ex b/apps/upi_dynamic/lib/upi_dynamic/controllers/api/v1/upi_controller.ex
index 5b2c723..43f0edd 100644
--- a/apps/upi_dynamic/lib/upi_dynamic/controllers/api/v1/upi_controller.ex
+++ b/apps/upi_dynamic/lib/upi_dynamic/controllers/api/v1/upi_controller.ex
@@ -225,9 +225,12 @@ defmodule UpiDynamic.Api.V1.UpiController do
                     # Send ACK immediately and return (do not continue with normal processing)
                     msg_id_str = parsed_data.msg_id || "unknown"
 
-                    ack_xml = """
-                    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqValQr" reqMsgId="#{msg_id_str}" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}"/>
-                    """
+                    # [ACK ReqValQr ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+                    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+                    # OLD:                     ack_xml = """
+                    # OLD:                     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqValQr" reqMsgId="#{msg_id_str}" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}"/>
+                    # OLD:                     """
+                    ack_xml = UpiCore.NpciFormat.ack_xml("ReqValQr", msg_id_str)
 
                     Logger.info("Sending ACK for ReqValQr with msgId=#{msg_id_str}")
                     Logger.info("ACK XML: #{ack_xml}")
@@ -282,9 +285,12 @@ defmodule UpiDynamic.Api.V1.UpiController do
     # Step 1: Generate ACK XML
     msg_id_str = parsed_data.msg_id || "unknown"
 
-    ack_xml = """
-    <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
-    """
+    # [ACK ReqValQr ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     ack_xml = """
+    # OLD:     <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqValQr", msg_id_str)
 
     # Log the ACK being sent
     Logger.info("Sending ACK for ReqValQr with msgId=#{msg_id_str}")
@@ -532,9 +538,12 @@ defmodule UpiDynamic.Api.V1.UpiController do
     # Step 1: Generate ACK XML
     msg_id_str = parsed_data.msg_id || "unknown"
 
-    ack_xml = """
-    <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
-    """
+    # [ACK ReqValQr ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     ack_xml = """
+    # OLD:     <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqValQr", msg_id_str)
 
     # Log the ACK being sent
     Logger.info("[StaticQR] Sending ACK for ReqValQr with msgId=#{msg_id_str}")
@@ -1575,25 +1584,30 @@ defmodule UpiDynamic.Api.V1.UpiController do
   end
 
   defp generate_resp_hbt_ack(parsed_resp_hbt) do
-    timestamp =
-      DateTime.utc_now()
-      |> DateTime.shift_zone!("Asia/Kolkata")
-      |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-    <ns2:Ack api="RespHbt" reqMsgId="#{parsed_resp_hbt.msg_id}" err="" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
-    """
+    # [ACK RespHbt Ack] CHANGED: standard Ack with IST seconds ts (was shifted UTC with microseconds and an extra err="" attribute)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp =
+    # OLD:       DateTime.utc_now()
+    # OLD:       |> DateTime.shift_zone!("Asia/Kolkata")
+    # OLD:       |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+    # OLD:     <ns2:Ack api="RespHbt" reqMsgId="#{parsed_resp_hbt.msg_id}" err="" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("RespHbt", parsed_resp_hbt.msg_id)
 
     Logger.info("✅ Generated RespHbt ACK with msgId: #{parsed_resp_hbt.msg_id}")
     {:ok, String.trim(ack_xml)}
   end
 
   defp generate_error_ack(api, msg_id, error_reason) do
-    timestamp =
-      DateTime.utc_now()
-      |> DateTime.shift_zone!("Asia/Kolkata")
-      |> DateTime.to_iso8601()
+    # [ACK error Ack ts] CHANGED: IST ts, whole seconds (err attribute kept - it carries the reason)
+    # OLD:     timestamp =
+    # OLD:       DateTime.utc_now()
+    # OLD:       |> DateTime.shift_zone!("Asia/Kolkata")
+    # OLD:       |> DateTime.to_iso8601()
+    timestamp = UpiCore.NpciFormat.ist_now()
 
     # Limit error message length
     error_msg = String.slice(to_string(error_reason), 0, 100)
@@ -3420,10 +3434,11 @@ defmodule UpiDynamic.Api.V1.UpiController do
     end
   end
 
-  defp generate_ack_response do
-    # Generate a simple ACK response for timeout simulation
-    ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" reqMsgId="TIMEOUT_SIM" err="00"/>|
-  end
+  # [ACK timeout-simulation Ack] REMOVED: generate_ack_response/0 is unused (dead code) and invented reqMsgId "TIMEOUT_SIM" and err="00" with a UTC ts
+  # OLD:   defp generate_ack_response do
+  # OLD:     # Generate a simple ACK response for timeout simulation
+  # OLD:     ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" reqMsgId="TIMEOUT_SIM" err="00"/>|
+  # OLD:   end
 
   defp generate_chk_txn_ack_response(xml_body) do
     # Extract reqMsgId from the request XML
@@ -3434,10 +3449,13 @@ defmodule UpiDynamic.Api.V1.UpiController do
       end
 
     # Use current timestamp
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    # Generate proper ACK response for ReqChkTxn
-    ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqChkTxn" reqMsgId="#{req_msg_id}" ts="#{timestamp}"/>|
+    # [ACK ReqChkTxn ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     # Generate proper ACK response for ReqChkTxn
+    # OLD:     ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqChkTxn" reqMsgId="#{req_msg_id}" ts="#{timestamp}"/>|
+    UpiCore.NpciFormat.ack_xml("ReqChkTxn", req_msg_id)
   end
 
   defp extract_msg_id_from_xml(xml_body) do
diff --git a/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_05_scenario.ex b/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_05_scenario.ex
index 91b4e59..7cb951a 100644
--- a/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_05_scenario.ex
+++ b/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_05_scenario.ex
@@ -63,11 +63,14 @@ defmodule UpiDynamic.SpecialHandlers.AccIp05Scenario do
 
     # Generate ACK XML
     msg_id = get_msg_id_from_parsed_data(parsed_data)
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" reqMsgId="#{msg_id}" ts="#{timestamp}"/>
-    """
+    # [ACK ACC_IP_05 ReqPay ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" reqMsgId="#{msg_id}" ts="#{timestamp}"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqPay", msg_id)
 
     Logger.info("🎯 ACCIP05: ACK generated successfully for msgId=#{msg_id}")
     Logger.debug("🎯 ACCIP05: ACK XML: #{ack_xml}")
diff --git a/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_08_scenario.ex b/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_08_scenario.ex
index 7f79d2e..c886ff4 100644
--- a/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_08_scenario.ex
+++ b/apps/upi_dynamic/lib/upi_dynamic/special_handlers/acc_ip_08_scenario.ex
@@ -204,15 +204,18 @@ defmodule UpiDynamic.SpecialHandlers.AccIp08Scenario do
 
   @doc false
   def generate_ack_response(xml_body, parsed_data) do
-    msg_id = Map.get(parsed_data, :msg_id) || generate_msg_id()
-
-    """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <Ack xmlns="http://npci.org/upi/schema/">
-      <Head ver="2.0" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" orgId="MERCURY001" msgId="#{msg_id}"/>
-      <Resp result="OK" code="00" desc="ACK"/>
-    </Ack>
-    """
+    # [ACK ACC_IP_08 ReqChkTxn Ack] CHANGED: the fake <Ack><Head>...<Resp result="OK"> document (orgId MERCURY001, UTC ts, generated msgId when missing) is replaced by the standard Ack of the request msgId
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     msg_id = Map.get(parsed_data, :msg_id) || generate_msg_id()
+    # OLD:
+    # OLD:     """
+    # OLD:     <?xml version="1.0" encoding="UTF-8"?>
+    # OLD:     <Ack xmlns="http://npci.org/upi/schema/">
+    # OLD:       <Head ver="2.0" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" orgId="MERCURY001" msgId="#{msg_id}"/>
+    # OLD:       <Resp result="OK" code="00" desc="ACK"/>
+    # OLD:     </Ack>
+    # OLD:     """
+    UpiCore.NpciFormat.ack_xml("ReqChkTxn", Map.get(parsed_data, :msg_id))
   end
 
   defp generate_and_send_x7_resp_chk_txn(xml_body, parsed_data) do
diff --git a/apps/upi_dynamic/test/upi_dynamic/ack_test.exs b/apps/upi_dynamic/test/upi_dynamic/ack_test.exs
new file mode 100644
index 0000000..e3414c7
--- /dev/null
+++ b/apps/upi_dynamic/test/upi_dynamic/ack_test.exs
@@ -0,0 +1,84 @@
+defmodule UpiDynamic.AckTest do
+  use ExUnit.Case, async: false
+
+  import Plug.Conn
+  import SweetXml
+
+  alias UpiDynamic.Api.V1.UpiController
+
+  @msg_id "NPCIREQMSG00000000000000000000001"
+  @ts_re ~r/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+05:30$/
+
+  @qr "upiGlobal://pay?ver=01&mode=16&pa=restaurant@mercury&pn=Dubai%20Fine%20Dining&mc=5812&bAm=5.00&bCurr=AED&QRexpire=2020-07-09T15:16:11+05:30&QRts=2020-07-09T14:46:11+05:30&sign=X"
+
+  setup do
+    {:ok, _} = Application.ensure_all_started(:upi_core)
+    pid = Ecto.Adapters.SQL.Sandbox.start_owner!(UpiCore.Repo, shared: true)
+    on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
+
+    # the controller asks this Agent whether a txn is a timeout simulation (the dynamic app starts its own copy under another name)
+    start_supervised!({UpiCore.SpecialHandlers.TimeoutSimulationHandler, []})
+
+    # never let the async RespValQr / RespChkTxn reach the real NPCI endpoint from a test
+    for key <- [:npci_qr_validation_endpoint, :npci_respchktxn_endpoint, :npci_resp_chk_txn_endpoint] do
+      old = Application.get_env(:upi_dynamic, key)
+      Application.put_env(:upi_dynamic, key, "http://127.0.0.1:9/")
+      on_exit(fn -> if old, do: Application.put_env(:upi_dynamic, key, old), else: Application.delete_env(:upi_dynamic, key) end)
+    end
+
+    :ok
+  end
+
+  defp call(action, path, xml) do
+    Plug.Test.conn(:post, path, xml)
+    |> put_req_header("content-type", "application/xml")
+    |> then(&apply(UpiController, action, [&1, %{}]))
+  end
+
+  defp assert_ack(conn, api) do
+    assert conn.status == 200
+    body = conn.resp_body
+    assert String.trim(body) =~ ~r|^<ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="#{api}" reqMsgId="#{@msg_id}" ts="[^"]+"/>$|
+    assert xpath(body, ~x"/*[local-name()='Ack']/@ts"s) =~ @ts_re
+    assert xpath(body, ~x"/*[local-name()='Ack']/@reqMsgId"s) == @msg_id
+  end
+
+  test "ReqValQr (normal flow) is acknowledged with the standard Ack" do
+    qr = String.replace(@qr, "QRexpire=2020-07-09T15:16:11+05:30", "QRexpire=2999-07-09T15:16:11+05:30")
+
+    xml = """
+    <ns2:ReqValQr xmlns:ns2="http://npci.org/upi/schema/">
+      <Head ver="2.0" ts="2026-07-09T14:46:20+05:30" orgId="NPCI" msgId="#{@msg_id}"/>
+      <Txn id="NPCITXN0001" note="N" refId="R" refUrl="http://x" ts="2026-07-09T14:46:21+05:30" type="IntlQr" custRef="CUSTREF01" initiationMode="16" purpose="11"/>
+      <Payer addr="payer@npci" name="tester" seqNum="1" type="PERSON" code="0000"/>
+      <Institution netInstId="MER1010001" QrPayLoad="#{String.replace(qr, "&", "&amp;")}" conCode="AE"/>
+    </ns2:ReqValQr>
+    """
+
+    assert_ack(call(:validate_qr, "/ReqValQr/2.0/urn:txnid:NPCITXN0001", xml), "ReqValQr")
+  end
+
+  test "ReqValQr with an expired QR (PE path) is acknowledged with the standard Ack" do
+    xml = """
+    <ns2:ReqValQr xmlns:ns2="http://npci.org/upi/schema/">
+      <Head ver="2.0" ts="2026-07-09T14:46:20+05:30" orgId="NPCI" msgId="#{@msg_id}"/>
+      <Txn id="NPCITXN0002" note="N" refId="R" refUrl="http://x" ts="2026-07-09T14:46:21+05:30" type="IntlQr" custRef="CUSTREF01" initiationMode="16" purpose="11"/>
+      <Payer addr="payer@npci" name="tester" seqNum="1" type="PERSON" code="0000"/>
+      <Institution netInstId="MER1010001" QrPayLoad="#{String.replace(@qr, "&", "&amp;")}" conCode="AE"/>
+    </ns2:ReqValQr>
+    """
+
+    assert_ack(call(:validate_qr, "/ReqValQr/2.0/urn:txnid:NPCITXN0002", xml), "ReqValQr")
+  end
+
+  test "ReqChkTxn is acknowledged with the request msgId and the standard Ack" do
+    xml = """
+    <ns2:ReqChkTxn xmlns:ns2="http://npci.org/upi/schema/">
+      <Head ver="2.0" ts="2026-07-09T14:46:20+05:30" orgId="NPCI" msgId="#{@msg_id}"/>
+      <Txn id="NPCICHK0001" note="N" refId="R" refUrl="http://x" ts="2026-07-09T14:46:21+05:30" type="ChkTxn" orgTxnId="ORGTXN0001" orgMsgId="NPCIORGMSG1" custRef="C" initiationMode="16" purpose="11" subType="PAY"/>
+    </ns2:ReqChkTxn>
+    """
+
+    assert_ack(call(:check_transaction, "/ReqChkTxn/2.0/urn:txnid:NPCICHK0001", xml), "ReqChkTxn")
+  end
+end
diff --git a/apps/upi_static/lib/upi_static/controllers/api/v1/upi_controller.ex b/apps/upi_static/lib/upi_static/controllers/api/v1/upi_controller.ex
index 22b5b76..1997111 100644
--- a/apps/upi_static/lib/upi_static/controllers/api/v1/upi_controller.ex
+++ b/apps/upi_static/lib/upi_static/controllers/api/v1/upi_controller.ex
@@ -224,9 +224,12 @@ defmodule UpiStatic.Api.V1.UpiController do
                     # Send ACK immediately and return (do not continue with normal processing)
                     msg_id_str = parsed_data.msg_id || "unknown"
 
-                    ack_xml = """
-                    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqValQr" reqMsgId="#{msg_id_str}" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}"/>
-                    """
+                    # [ACK ReqValQr ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+                    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+                    # OLD:                     ack_xml = """
+                    # OLD:                     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqValQr" reqMsgId="#{msg_id_str}" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}"/>
+                    # OLD:                     """
+                    ack_xml = UpiCore.NpciFormat.ack_xml("ReqValQr", msg_id_str)
 
                     Logger.info("Sending ACK for ReqValQr with msgId=#{msg_id_str}")
                     Logger.info("ACK XML: #{ack_xml}")
@@ -281,9 +284,12 @@ defmodule UpiStatic.Api.V1.UpiController do
     # Step 1: Generate ACK XML
     msg_id_str = parsed_data.msg_id || "unknown"
 
-    ack_xml = """
-    <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
-    """
+    # [ACK ReqValQr ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     ack_xml = """
+    # OLD:     <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqValQr", msg_id_str)
 
     # Log the ACK being sent
     Logger.info("Sending ACK for ReqValQr with msgId=#{msg_id_str}")
@@ -531,9 +537,12 @@ defmodule UpiStatic.Api.V1.UpiController do
     # Step 1: Generate ACK XML
     msg_id_str = parsed_data.msg_id || "unknown"
 
-    ack_xml = """
-    <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
-    """
+    # [ACK ReqValQr ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     ack_xml = """
+    # OLD:     <ns2:Ack xmlns:ns2=\"http://npci.org/upi/schema/\" api=\"ReqValQr\" reqMsgId=\"#{msg_id_str}\" ts=\"#{DateTime.utc_now() |> DateTime.to_iso8601()}\"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqValQr", msg_id_str)
 
     # Log the ACK being sent
     Logger.info("[StaticQR] Sending ACK for ReqValQr with msgId=#{msg_id_str}")
@@ -1574,25 +1583,30 @@ defmodule UpiStatic.Api.V1.UpiController do
   end
 
   defp generate_resp_hbt_ack(parsed_resp_hbt) do
-    timestamp =
-      DateTime.utc_now()
-      |> DateTime.shift_zone!("Asia/Kolkata")
-      |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-    <ns2:Ack api="RespHbt" reqMsgId="#{parsed_resp_hbt.msg_id}" err="" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
-    """
+    # [ACK RespHbt Ack] CHANGED: standard Ack with IST seconds ts (was shifted UTC with microseconds and an extra err="" attribute)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp =
+    # OLD:       DateTime.utc_now()
+    # OLD:       |> DateTime.shift_zone!("Asia/Kolkata")
+    # OLD:       |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+    # OLD:     <ns2:Ack api="RespHbt" reqMsgId="#{parsed_resp_hbt.msg_id}" err="" ts="#{timestamp}" xmlns:ns2="http://npci.org/upi/schema/"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("RespHbt", parsed_resp_hbt.msg_id)
 
     Logger.info("✅ Generated RespHbt ACK with msgId: #{parsed_resp_hbt.msg_id}")
     {:ok, String.trim(ack_xml)}
   end
 
   defp generate_error_ack(api, msg_id, error_reason) do
-    timestamp =
-      DateTime.utc_now()
-      |> DateTime.shift_zone!("Asia/Kolkata")
-      |> DateTime.to_iso8601()
+    # [ACK error Ack ts] CHANGED: IST ts, whole seconds (err attribute kept - it carries the reason)
+    # OLD:     timestamp =
+    # OLD:       DateTime.utc_now()
+    # OLD:       |> DateTime.shift_zone!("Asia/Kolkata")
+    # OLD:       |> DateTime.to_iso8601()
+    timestamp = UpiCore.NpciFormat.ist_now()
 
     # Limit error message length
     error_msg = String.slice(to_string(error_reason), 0, 100)
@@ -3362,10 +3376,11 @@ defmodule UpiStatic.Api.V1.UpiController do
     end
   end
 
-  defp generate_ack_response do
-    # Generate a simple ACK response for timeout simulation
-    ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" reqMsgId="TIMEOUT_SIM" err="00"/>|
-  end
+  # [ACK timeout-simulation Ack] REMOVED: generate_ack_response/0 is unused (dead code) and invented reqMsgId "TIMEOUT_SIM" and err="00" with a UTC ts
+  # OLD:   defp generate_ack_response do
+  # OLD:     # Generate a simple ACK response for timeout simulation
+  # OLD:     ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" reqMsgId="TIMEOUT_SIM" err="00"/>|
+  # OLD:   end
 
   defp generate_chk_txn_ack_response(xml_body) do
     # Extract reqMsgId from the request XML
@@ -3376,10 +3391,13 @@ defmodule UpiStatic.Api.V1.UpiController do
       end
 
     # Use current timestamp
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    # Generate proper ACK response for ReqChkTxn
-    ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqChkTxn" reqMsgId="#{req_msg_id}" ts="#{timestamp}"/>|
+    # [ACK ReqChkTxn ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     # Generate proper ACK response for ReqChkTxn
+    # OLD:     ~s|<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqChkTxn" reqMsgId="#{req_msg_id}" ts="#{timestamp}"/>|
+    UpiCore.NpciFormat.ack_xml("ReqChkTxn", req_msg_id)
   end
 
   defp extract_msg_id_from_xml(xml_body) do
diff --git a/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_05_scenario.ex b/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_05_scenario.ex
index 4248cb1..999c969 100644
--- a/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_05_scenario.ex
+++ b/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_05_scenario.ex
@@ -84,11 +84,14 @@ defmodule UpiStatic.SpecialHandlers.StaticAccIp05Scenario do
 
     # Generate ACK XML
     msg_id = get_msg_id_from_parsed_data(parsed_data)
-    timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
-
-    ack_xml = """
-    <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" reqMsgId="#{msg_id}" ts="#{timestamp}"/>
-    """
+    # [ACK ACC_IP_05 (static) ReqPay ts] CHANGED: standard Ack with IST ts (was UTC with microseconds)
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     timestamp = DateTime.utc_now() |> DateTime.to_iso8601()
+    # OLD:
+    # OLD:     ack_xml = """
+    # OLD:     <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" reqMsgId="#{msg_id}" ts="#{timestamp}"/>
+    # OLD:     """
+    ack_xml = UpiCore.NpciFormat.ack_xml("ReqPay", msg_id)
 
     Logger.info("🎯 STATIC_ACCIP05: ACK generated successfully for msgId=#{msg_id}")
     Logger.debug("🎯 STATIC_ACCIP05: ACK XML: #{ack_xml}")
diff --git a/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_08_scenario.ex b/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_08_scenario.ex
index d7b7bb3..695ab2b 100644
--- a/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_08_scenario.ex
+++ b/apps/upi_static/lib/upi_static/special_handlers/static_acc_ip_08_scenario.ex
@@ -363,15 +363,18 @@ defmodule UpiStatic.SpecialHandlers.StaticAccIp08Scenario do
   end
 
   defp generate_ack_response(_xml_body, parsed_data) do
-    msg_id = Map.get(parsed_data, :msg_id) || generate_msg_id()
-
-    """
-    <?xml version="1.0" encoding="UTF-8"?>
-    <Ack xmlns="http://npci.org/upi/schema/">
-      <Head ver="2.0" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" orgId="#{@static_org_id}" msgId="#{msg_id}"/>
-      <Resp result="OK" code="00" desc="ACK"/>
-    </Ack>
-    """
+    # [ACK ACC_IP_08 ReqChkTxn Ack] CHANGED: the fake <Ack><Head>...<Resp result="OK"> document (orgId MERCURY001, UTC ts, generated msgId when missing) is replaced by the standard Ack of the request msgId
+    # TSD §7.1: Ack = <ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="<ReqApi>" reqMsgId="<request msgId>" ts="<IST seconds>"/>
+    # OLD:     msg_id = Map.get(parsed_data, :msg_id) || generate_msg_id()
+    # OLD:
+    # OLD:     """
+    # OLD:     <?xml version="1.0" encoding="UTF-8"?>
+    # OLD:     <Ack xmlns="http://npci.org/upi/schema/">
+    # OLD:       <Head ver="2.0" ts="#{DateTime.utc_now() |> DateTime.to_iso8601()}" orgId="#{@static_org_id}" msgId="#{msg_id}"/>
+    # OLD:       <Resp result="OK" code="00" desc="ACK"/>
+    # OLD:     </Ack>
+    # OLD:     """
+    UpiCore.NpciFormat.ack_xml("ReqChkTxn", Map.get(parsed_data, :msg_id))
   end
 
   defp generate_and_send_x7_resp_chk_txn(_xml_body, parsed_data) do
diff --git a/apps/upi_static/test/upi_static/ack_test.exs b/apps/upi_static/test/upi_static/ack_test.exs
new file mode 100644
index 0000000..ac4c8e7
--- /dev/null
+++ b/apps/upi_static/test/upi_static/ack_test.exs
@@ -0,0 +1,72 @@
+defmodule UpiStatic.AckTest do
+  use ExUnit.Case, async: false
+
+  import Plug.Conn
+  import SweetXml
+
+  alias UpiStatic.Api.V1.UpiController
+
+  @msg_id "NPCIREQMSG00000000000000000000002"
+  @ts_re ~r/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\+05:30$/
+
+  @static_qr "upi://pay?ver=01&mode=02&pa=shop@mercury&pn=Shop%20One&mc=5812&mid=SHOPMID01&mtid=T1&QRexpire=2999-04-29T14:45:05+05:30&sign=X"
+
+  setup do
+    {:ok, _} = Application.ensure_all_started(:upi_core)
+    pid = Ecto.Adapters.SQL.Sandbox.start_owner!(UpiCore.Repo, shared: true)
+    on_exit(fn -> Ecto.Adapters.SQL.Sandbox.stop_owner(pid) end)
+
+    # the static controller also asks the UpiCore copy of this Agent (the app only starts its own, differently named one)
+    start_supervised!({UpiCore.SpecialHandlers.TimeoutSimulationHandler, []})
+
+    for key <- [:npci_qr_validation_endpoint, :npci_respchktxn_endpoint, :npci_resp_chk_txn_endpoint] do
+      old = Application.get_env(:upi_static, key)
+      Application.put_env(:upi_static, key, "http://127.0.0.1:9/")
+      on_exit(fn -> if old, do: Application.put_env(:upi_static, key, old), else: Application.delete_env(:upi_static, key) end)
+    end
+
+    :ok
+  end
+
+  defp call(action, path, xml) do
+    Plug.Test.conn(:post, path, xml)
+    |> put_req_header("content-type", "application/xml")
+    |> then(&apply(UpiController, action, [&1, %{}]))
+  end
+
+  defp assert_ack(conn, api) do
+    assert conn.status == 200
+    body = conn.resp_body
+    assert String.trim(body) =~ ~r|^<ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="#{api}" reqMsgId="#{@msg_id}" ts="[^"]+"/>$|
+    assert xpath(body, ~x"/*[local-name()='Ack']/@ts"s) =~ @ts_re
+  end
+
+  test "static ReqValQr (mode 02) is acknowledged with the standard Ack" do
+    xml = """
+    <ns2:ReqValQr xmlns:ns2="http://npci.org/upi/schema/">
+      <Head ver="2.0" ts="2026-07-09T14:46:20+05:30" orgId="NPCI" msgId="#{@msg_id}"/>
+      <Txn id="NPCITXN0003" note="N" refId="R" refUrl="http://x" ts="2026-07-09T14:46:21+05:30" type="IntlQr" custRef="CUSTREF02" initiationMode="02" purpose="11"/>
+      <Payer addr="payer@npci" name="tester" seqNum="1" type="PERSON" code="0000"/>
+      <Institution netInstId="MER1020002" QrPayLoad="#{String.replace(@static_qr, "&", "&amp;")}" conCode="AE"/>
+    </ns2:ReqValQr>
+    """
+
+    assert_ack(call(:validate_qr, "/ReqValQr/2.0/urn:txnid:NPCITXN0003", xml), "ReqValQr")
+  end
+
+  test "static ReqChkTxn is acknowledged with the request msgId and the standard Ack" do
+    xml = """
+    <ns2:ReqChkTxn xmlns:ns2="http://npci.org/upi/schema/">
+      <Head ver="2.0" ts="2026-07-09T14:46:20+05:30" orgId="NPCI" msgId="#{@msg_id}"/>
+      <Txn id="NPCICHK0002" note="N" refId="R" refUrl="http://x" ts="2026-07-09T14:46:21+05:30" type="ChkTxn" orgTxnId="ORGTXN0002" orgMsgId="NPCIORGMSG2" custRef="CUSTREF02" initiationMode="02" purpose="11" subType="PAY"/>
+    </ns2:ReqChkTxn>
+    """
+
+    assert_ack(call(:check_transaction, "/ReqChkTxn/2.0/urn:txnid:NPCICHK0002", xml), "ReqChkTxn")
+  end
+
+  test "static ACC_IP_05 Ack is the standard Ack" do
+    {:ok, ack} = UpiStatic.SpecialHandlers.StaticAccIp05Scenario.handle_req_pay_immediate_ack("<ReqPay/>", %{msg_id: @msg_id})
+    assert String.trim(ack) =~ ~r|^<ns2:Ack xmlns:ns2="http://npci.org/upi/schema/" api="ReqPay" reqMsgId="#{@msg_id}" ts="[^"]+"/>$|
+  end
+end
