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 8e0d1b7..aeee0e2 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
@@ -283,8 +283,11 @@ defmodule UpiCore.SpecialHandlers.AccIp05Scenario do
       ref_id: Map.get(parsed_data, :ref_id, ""),
       ref_url: "https://mercurypay.aritic.app",
       expire_ts: Map.get(parsed_data, :expire_ts),
-      qr_ver: Map.get(parsed_data, :qr_ver, "02"),
-      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (02 / 03); generate_resp_pay echoes the ReqPay QR element
+      # OLD:       qr_ver: Map.get(parsed_data, :qr_ver, "02"),
+      # OLD:       qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      qr_ver: Map.get(parsed_data, :qr_ver),
+      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, Map.get(parsed_data, :qr_medium)),
       sp_risk_score: "0",
       npci_risk_score: Map.get(parsed_data, :npci_risk_score, "0"),
 
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 d46524b..68a7634 100644
--- a/apps/upi_core/lib/upi_core/upi_xml_schema.ex
+++ b/apps/upi_core/lib/upi_core/upi_xml_schema.ex
@@ -86,14 +86,15 @@ defmodule UpiCore.UpiXmlSchema do
     "ZR" => "Merchant not found"
   }
 
-  defp extract_country_code_from_qr(qr_payload, response_data) do
-    # Extract mLoc (merchant location / country code) from QR payload
-    # Format: &mLoc=AE or ?mLoc=AE (2-letter country code)
-    case Regex.run(~r/[&?]mLoc=([A-Z]{2})/, qr_payload || "") do
-      [_, code] -> code
-      _ -> Map.get(response_data, :country_code) || "AE"  # Fallback to country_code from response_data, then default to AE
-    end
-  end
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_country_code_from_qr is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   defp extract_country_code_from_qr(qr_payload, response_data) do
+  # OLD:     # Extract mLoc (merchant location / country code) from QR payload
+  # OLD:     # Format: &mLoc=AE or ?mLoc=AE (2-letter country code)
+  # OLD:     case Regex.run(~r/[&?]mLoc=([A-Z]{2})/, qr_payload || "") do
+  # OLD:       [_, code] -> code
+  # OLD:       _ -> Map.get(response_data, :country_code) || "AE"  # Fallback to country_code from response_data, then default to AE
+  # OLD:     end
+  # OLD:   end
 
   # [RespValQr determine_initiation_mode] REMOVED: initiationMode is echoed from the request, never guessed (was 16 / 01 from QR contents)
   # TSD Table 2: Txn initiationMode = value received in ReqValQr; static flow only for 01/02
@@ -579,6 +580,34 @@ defmodule UpiCore.UpiXmlSchema do
   end
   def extract_ref_data_from_req_pay(_), do: {:ok, %{}}
 
+  # [RespPay <Resp> template] OLD TEMPLATE kept for reference - removed <Merchant>/<Institution> (TSD Table 4 has only <Ref> in <Resp>); Txn / QR / Ref now echo ReqPay (see the tagged lines above)
+  # OLD:   xml_body = """
+  # OLD: <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+  # OLD: <upi:RespPay xmlns:upi="http://npci.org/upi/schema/">
+  # OLD:   <Head ver="2.0" ts="#{Map.get(response_data, :head_ts, get_timestamp())}" orgId="#{Map.get(response_data, :org_id)}" msgId="#{generate_fixed_length_msg_id(Map.get(response_data, :msg_id))}"/>
+  # OLD:   <Txn id="#{txn_id}"
+  # OLD:        note="#{validate_and_preserve_note(Map.get(request_source, :note) || Map.get(response_data, :note))}"
+  # OLD:        refId="#{Map.get(request_source, :ref_id) || Map.get(response_data, :ref_id) || ""}"
+  # OLD:        custRef="#{Map.get(request_source, :cust_ref) || Map.get(response_data, :cust_ref)}"
+  # OLD:        refUrl="#{get_psp_ref_url()}"
+  # OLD:        ts="#{Map.get(request_source, :txn_ts) || Map.get(response_data, :txn_ts, get_timestamp())}"
+  # OLD:        purpose="#{Map.get(request_source, :purpose) || Map.get(response_data, :purpose, "11") }"
+  # OLD:        type="#{Map.get(request_source, :txn_type) || Map.get(response_data, :txn_type)}"
+  # OLD:        subType="#{Map.get(request_source, :sub_type) || Map.get(response_data, :sub_type, "PAY") }"
+  # OLD:        initiationMode="#{Map.get(request_source, :initiation_mode) || Map.get(response_data, :initiation_mode, "01") }"
+  # OLD:        orgTxnId="#{Map.get(request_source, :org_txn_id) || Map.get(response_data, :org_txn_id) || ""}"
+  # OLD:        refCategory="#{Map.get(request_source, :ref_category) || Map.get(response_data, :ref_category, "00") }">
+  # OLD:     <RiskScores>
+  # OLD:       <Score provider="NPCI" type="TXNRISK" value="00999"/>
+  # OLD:     </RiskScores>
+  # OLD:     <QR expireTs="#{qr_expire_ts}" qVer="#{qr_ver}" qrMedium="#{qr_medium}"#{if qr_ts_attribute, do: " ts=\"#{qr_ts_attribute}\"", else: ""} />
+  # OLD:   </Txn>
+  # OLD:   <Resp reqMsgId="#{resp_req_msg_id || ""}" result="#{Map.get(response_data, :result)}"#{if Map.get(response_data, :result) != "SUCCESS", do: " errCode=\"#{err_code}\"", else: ""}>
+  # OLD:     <Ref type="PAYEE" seqNum="1" addr="#{payee_addr}" code="#{Map.get(response_data, :payee_code)}" orgAmount="#{org_amount}" respCode="#{Map.get(response_data, :resp_code) || ""}" regName="#{Map.get(response_data, :reg_name)}" IFSC="#{Map.get(response_data, :ifsc)}" acNum="#{Map.get(response_data, :ac_num)}" accType="#{Map.get(response_data, :acc_type) || ""}" approvalNum="#{approval_num_truncated}" settAmount="#{sett_amount_final}" settCurrency="#{Map.get(response_data, :sett_currency) || Map.get(response_data, "sett_currency") || "INR"}" />
+  # OLD:     #{merchant_section}
+  # OLD:     #{institution_section}
+  # OLD:   </Resp>
+  # OLD: """
   @doc """
   Generate International Credit Response (RespPay) XML response
   Follows NPCI 2.0 specification with settlement details and approval codes
@@ -606,42 +635,85 @@ defmodule UpiCore.UpiXmlSchema do
 
       # Ensure Txn id: prefer the original ReqPay Txn id verbatim when available.
       # NPCI expects the RespPay Txn.id to match the ReqPay Txn.id exactly.
-      raw_txn_id = fetch_val.([:txn_id, "txn_id", :id, "id", :org_txn_id, "org_txn_id"], nil)
+      # [RespPay Txn.id] CHANGED: echo the ReqPay Txn id exactly (no trim / cut to 35, no org_txn_id fallback, no generated id)
+      # TSD Table 4: Txn id in RespPay = Txn id received in ReqPay
+      # OLD:       raw_txn_id = fetch_val.([:txn_id, "txn_id", :id, "id", :org_txn_id, "org_txn_id"], nil)
+      # OLD:       txn_id =
+      # OLD:         case raw_txn_id do
+      # OLD:           id when is_binary(id) and id != "" -> String.slice(String.trim(id), 0, 35)
+      # OLD:           id when is_integer(id) -> to_string(id)
+      # OLD:           _ -> generate_msg_id_like_sample()
+      # OLD:         end
+      raw_txn_id = fetch_val.([:txn_id, "txn_id"], nil)
       txn_id =
         case raw_txn_id do
-          id when is_binary(id) and id != "" -> String.slice(String.trim(id), 0, 35)
+          id when is_binary(id) and id != "" -> id
           id when is_integer(id) -> to_string(id)
-          _ -> generate_msg_id_like_sample()
+          _ ->
+            Logger.error("[RespPay Txn.id] ReqPay Txn id is missing - nothing to echo")
+            ""
         end
 
+      # [RespPay Txn / QR attributes] ADDED: everything echoed from ReqPay is read through req_val (request_data first) and XML-escaped
+      req_val = fn key -> Map.get(request_source, key) || Map.get(response_data, key) end
+      attr_always = fn name, value -> " #{name}=\"#{NpciFormat.xml_attr(value)}\"" end
+      result_final = Map.get(response_data, :result)
+
     # Prefer QR attributes from the original request to avoid NPCI mismatches
     # CRITICAL: NEVER generate default expiry timestamp for RespPay - must match original ReqPay exactly
     # QR variables needed for RespPay
-    qr_expire_ts = case Map.get(request_source, :expire_ts) || Map.get(response_data, :expire_ts) do
-      nil ->
-        require Logger
-        Logger.error("CRITICAL ERROR: QR ExpireTs missing from original ReqPay request. This will cause NPCI rejection 'CZ:TXN QR ExpireTs Not Match'")
-        generate_default_qr_expire_ts()
-      expire_ts -> expire_ts
+    # [RespPay QR.expireTs / QR.qVer / QR.qrMedium / QR.ts] CHANGED: echo the ReqPay QR element; the generated fallbacks (now+5min, 2.0 / 01, 04 / 03) are gone
+    # [RespPay QR.ts] CHANGED: ReqPay QR@ts (was Txn ts); [RespPay QR.query] REMOVED: generated and never sent
+    # TSD Table 4: QR expireTs / qVer / qrMedium / ts = values received in ReqPay
+    # OLD:     qr_expire_ts = case Map.get(request_source, :expire_ts) || Map.get(response_data, :expire_ts) do
+    # OLD:       nil ->
+    # OLD:         require Logger
+    # OLD:         Logger.error("CRITICAL ERROR: QR ExpireTs missing from original ReqPay request. This will cause NPCI rejection 'CZ:TXN QR ExpireTs Not Match'")
+    # OLD:         generate_default_qr_expire_ts()
+    # OLD:       expire_ts -> expire_ts
+    # OLD:     end
+    # OLD:     qr_ver = Map.get(request_source, :qr_ver) || Map.get(response_data, :qr_ver) || generate_default_qr_ver()
+    # OLD:     qr_medium = Map.get(request_source, :qr_medium) || Map.get(response_data, :qr_medium) || generate_default_qr_medium()
+    # OLD:     original_qr_ts = Map.get(request_source, :qr_ts)
+    # OLD:     qr_ts_attribute = if original_qr_ts && original_qr_ts != "" do
+    # OLD:       Map.get(request_source, :txn_ts) || original_qr_ts || extract_qr_ts_from_payload(request_source)
+    # OLD:     else
+    # OLD:       nil
+    # OLD:     end
+    # OLD:     qr_query = Map.get(request_source, :qr_query) || Map.get(response_data, :qr_query) || generate_default_qr_query()
+    qr_expire_ts = req_val.(:expire_ts)
+
+    if qr_expire_ts in [nil, ""] do
+      Logger.error("[RespPay QR.expireTs] ReqPay QR expireTs is missing - sending it empty (NPCI will reject: CZ)")
     end
-    qr_ver = Map.get(request_source, :qr_ver) || Map.get(response_data, :qr_ver) || generate_default_qr_ver()
-    qr_medium = Map.get(request_source, :qr_medium) || Map.get(response_data, :qr_medium) || generate_default_qr_medium()
-    original_qr_ts = Map.get(request_source, :qr_ts)
-    qr_ts_attribute = if original_qr_ts && original_qr_ts != "" do
-      Map.get(request_source, :txn_ts) || original_qr_ts || extract_qr_ts_from_payload(request_source)
-    else
-      nil
-    end
-    qr_query = Map.get(request_source, :qr_query) || Map.get(response_data, :qr_query) || generate_default_qr_query()
+
+    qr_ver = req_val.(:qr_ver)
+    qr_medium = req_val.(:qr_medium)
+    qr_ts_attribute = req_val.(:qr_ts)
 
     # # CRITICAL: STAN must be extracted from original request to avoid NPCI STAN MISMATCH
     # stan = Map.get(request_source, :stan) || Map.get(response_data, :stan) ||
     #        extract_stan_from_qr_payload(Map.get(request_source, :qr_payload) || Map.get(response_data, :qr_payload)) ||
     #        generate_deterministic_stan(Map.get(request_source, :txn_id) || Map.get(response_data, :txn_id))
 
-    # Ensure we always emit a Resp.ErrorCode element (NPCI mandates RESP.ERRORCODE)
-    err_code = Map.get(response_data, :err_code) || Map.get(response_data, :errCode) ||
-               (if Map.get(response_data, :result) == "SUCCESS", do: "00", else: "02")
+    # [RespPay Resp.errCode / Ref.respCode] CHANGED: on FAILURE errCode is exactly the Ref respCode; the "02" default is gone, on SUCCESS there is no errCode
+    # TSD Table 4: Resp errCode only when result=FAILURE and equal to Ref respCode
+    # OLD:     # Ensure we always emit a Resp.ErrorCode element (NPCI mandates RESP.ERRORCODE)
+    # OLD:     err_code = Map.get(response_data, :err_code) || Map.get(response_data, :errCode) ||
+    # OLD:                (if Map.get(response_data, :result) == "SUCCESS", do: "00", else: "02")
+    ref_resp_code =
+      if result_final == "SUCCESS" do
+        Map.get(response_data, :resp_code) || ""
+      else
+        code =
+          Map.get(response_data, :resp_code) || Map.get(response_data, :err_code) ||
+            Map.get(response_data, :errCode) || ""
+
+        if code == "", do: Logger.error("[RespPay Resp.errCode] FAILURE without respCode / errCode - nothing to send")
+        code
+      end
+
+    err_code = ref_resp_code
 
     # Sanitize mandatory Ref fields to meet NPCI validation:
     # seqNum must be numeric; addr must be alphanumeric; approvalNum must be present
@@ -649,30 +721,56 @@ defmodule UpiCore.UpiXmlSchema do
     ref_seq = ref_seq_raw |> to_string() |> String.replace(~r/[^0-9]/, "")
     ref_seq = if ref_seq == "", do: generate_numeric_seq_num(), else: ref_seq
 
-    payee_addr_raw = Map.get(response_data, :payee_addr) || Map.get(request_source, :payee_addr) || Map.get(response_data, :addr) || ""
-    # E09 fix: Allow UPI VPA characters (@, ., _, -) in address to meet NPCI validation
-    payee_addr = payee_addr_raw |> to_string() |> String.replace(~r/[^A-Za-z0-9@._-]/, "")
-    payee_addr = if payee_addr == "", do: (Map.get(response_data, :ac_num) || Map.get(request_source, :ac_num) || "") |> to_string() |> String.replace(~r/[^A-Za-z0-9@._-]/, ""), else: payee_addr
+    # [RespPay Ref.addr] CHANGED: ReqPay Payee addr as received (the regex that dropped characters and the acNum fallback are gone)
+    # TSD Table 4: Ref addr = Payee addr of ReqPay
+    # OLD:     payee_addr_raw = Map.get(response_data, :payee_addr) || Map.get(request_source, :payee_addr) || Map.get(response_data, :addr) || ""
+    # OLD:     # E09 fix: Allow UPI VPA characters (@, ., _, -) in address to meet NPCI validation
+    # OLD:     payee_addr = payee_addr_raw |> to_string() |> String.replace(~r/[^A-Za-z0-9@._-]/, "")
+    # OLD:     payee_addr = if payee_addr == "", do: (Map.get(response_data, :ac_num) || Map.get(request_source, :ac_num) || "") |> to_string() |> String.replace(~r/[^A-Za-z0-9@._-]/, ""), else: payee_addr
+    payee_addr = req_val.(:payee_addr) || Map.get(response_data, :addr) || ""
 
     approval_num_final = Map.get(response_data, :approval_num) || Map.get(request_source, :approval_num) || generate_approval_num()
 
-    # Amount matching logic: settAmount uses sett_amount (INR am=) when present, else orgAmount
+    # [RespPay Ref.orgAmount / Ref.settAmount] CHANGED: SUCCESS -> orgAmount = format_amount(ReqPay Payee Amount value), settAmount = orgAmount; FAILURE -> settAmount 0.00
+    # [RespPay Ref.orgAmount FAILURE] UNCHANGED (waiting for NIPL): same expression as before, the caller decides (PE 0.00 / X7, YH, IV 5.00)
+    # TSD Table 4: settAmount = orgAmount on success
+    # OLD:     # Amount matching logic: settAmount uses sett_amount (INR am=) when present, else orgAmount
+    # OLD:     org_amount =
+    # OLD:       Map.get(response_data, :org_amount) ||
+    # OLD:       Map.get(response_data, :amount) ||
+    # OLD:       Map.get(response_data, :payee_amount) ||
+    # OLD:       Map.get(response_data, :base_amount) ||
+    # OLD:       "0.00"
+    # OLD:     sett_amount_final =
+    # OLD:       if Map.get(response_data, :result) == "FAILURE" do
+    # OLD:         "0.00"
+    # OLD:       else
+    # OLD:         # Use sett_amount (am= INR amount) when present; fallback to org_amount (bAm)
+    # OLD:         Map.get(response_data, :sett_amount) ||
+    # OLD:           Map.get(response_data, "sett_amount") ||
+    # OLD:           org_amount
+    # OLD:       end
     org_amount =
-      Map.get(response_data, :org_amount) ||
-      Map.get(response_data, :amount) ||
-      Map.get(response_data, :payee_amount) ||
-      Map.get(response_data, :base_amount) ||
-      "0.00"
-    sett_amount_final =
-      if Map.get(response_data, :result) == "FAILURE" do
-        "0.00"
+      if result_final == "SUCCESS" do
+        NpciFormat.format_amount(Map.get(request_source, :payee_amount)) ||
+          NpciFormat.format_amount(
+            Map.get(response_data, :org_amount) || Map.get(response_data, :amount) ||
+              Map.get(response_data, :payee_amount) || Map.get(response_data, :base_amount)
+          ) ||
+          (
+            Logger.error("[RespPay Ref.orgAmount] ReqPay Payee Amount is missing on a SUCCESS response")
+            ""
+          )
       else
-        # Use sett_amount (am= INR amount) when present; fallback to org_amount (bAm)
-        Map.get(response_data, :sett_amount) ||
-          Map.get(response_data, "sett_amount") ||
-          org_amount
+        Map.get(response_data, :org_amount) ||
+          Map.get(response_data, :amount) ||
+          Map.get(response_data, :payee_amount) ||
+          Map.get(response_data, :base_amount) ||
+          "0.00"
       end
 
+    sett_amount_final = if result_final == "SUCCESS", do: org_amount, else: "0.00"
+
     # Ensure approvalNum is alphanumeric max 6 chars (remove special chars, keep only letters and digits)
     approval_num_truncated =
       approval_num_final
@@ -683,82 +781,70 @@ defmodule UpiCore.UpiXmlSchema do
   # NPCI expects Resp.reqMsgId == ReqPay.Head.msgId. Try common key names.
   resp_req_msg_id = fetch_val.([:req_msg_id, "req_msg_id", :reqMsgId, "reqMsgId", :msg_id, "msg_id", :msgId, "msgId"], Map.get(response_data, :req_msg_id))
 
-  # Extract merchant details for Institution/Merchant section
-  merchant_brand = Map.get(response_data, :merchant_brand) || Map.get(response_data, :reg_name) || ""
-  merchant_legal_name = Map.get(response_data, :merchant_legal_name) || merchant_brand || ""
-  merchant_franchise = Map.get(response_data, :merchant_franchise) || ""
-  ownership_type = Map.get(response_data, :ownership_type) || "PRIVATE"
-  invoice_name = Map.get(response_data, :invoice_name) || merchant_brand || ""
-  invoice_number = Map.get(response_data, :invoice_number) || ""
-  invoice_date = Map.get(response_data, :invoice_date) || get_timestamp()
-
-  # Extract merchant identifiers for Identifier tag
-  merchant_sub_code = Map.get(response_data, :sub_code) || Map.get(response_data, :identifier_sub_code, "0000")
-  merchant_mid = extract_mid_from_qr(request_source) || Map.get(response_data, :identifier_mid, "")
-  merchant_sid = extract_msid_from_qr(request_source) || Map.get(response_data, :identifier_sid, "")
-  merchant_tid = extract_mtid_from_qr(request_source) || Map.get(response_data, :identifier_tid, "")
-  merchant_type = extract_merchant_type_from_qr(request_source) || Map.get(response_data, :merchant_type, "SMALL")
-  merchant_genre = extract_merchant_genre_from_qr(request_source) || Map.get(response_data, :merchant_genre, "RETAIL")
-  merchant_onboarding = extract_merchant_onboarding_from_qr(request_source) || Map.get(response_data, :onboarding_type, "AGGREGATOR")
-  merchant_loc = extract_merchant_loc_from_qr(request_source) || Map.get(response_data, :merchant_loc, "IN")
-  merchant_inst_code = Map.get(response_data, :identifier_inst_code) ||
-                        Map.get(response_data, :merchant_inst_code, "")
-
-  # Build Identifier tag only if there's merchant data
-  identifier_section = if merchant_mid != "" || merchant_sid != "" || merchant_tid != "" do
-    "<Identifier subCode=\"#{merchant_sub_code}\" mid=\"#{merchant_mid}\" sid=\"#{merchant_sid}\" tid=\"#{merchant_tid}\" merchantType=\"#{merchant_type}\" merchantGenre=\"#{merchant_genre}\" onBoardingType=\"#{merchant_onboarding}\" merchantLoc=\"#{merchant_loc}\" merchantInstCode=\"#{merchant_inst_code}\"/>"
-  else
-    ""
-  end
-
-  # Build Merchant section
-  merchant_section = if identifier_section != "" || merchant_brand != "" do
-    "<Merchant>" <>
-    identifier_section <>
-    "<Name brand=\"#{merchant_brand}\" legal=\"#{merchant_legal_name}\" franchise=\"#{merchant_franchise}\"/>" <>
-    "<Ownership type=\"#{ownership_type}\"/>" <>
-    "<Invoice name=\"#{invoice_name}\" num=\"#{invoice_number}\" date=\"#{invoice_date}\"/>" <>
-    "</Merchant>"
-  else
-    ""
-  end
-
-  # Build Institution section
-  qr_payload_for_resp = Map.get(request_source, :qr_payload) || Map.get(response_data, :qr_payload) || ""
-  net_inst_id = Map.get(request_source, :net_inst_id) || Map.get(response_data, :net_inst_id, "MER1010001")
-  institution_con_code = extract_country_code_from_qr(qr_payload_for_resp, request_source) || "IN"
-
-  institution_section = if merchant_section != "" || qr_payload_for_resp != "" do
-    "<Institution netInstId=\"#{net_inst_id}\" QrPayLoad=\"#{escape_xml_entities(qr_payload_for_resp)}\" conCode=\"#{institution_con_code}\"/>"
-  else
-    ""
-  end
+  # [RespPay <Merchant>/<Institution>] REMOVED: not part of RespPay <Resp> (TSD Table 4 has only <Ref>); the merchant_* / institution_* values are no longer built
+  # OLD:   # Extract merchant details for Institution/Merchant section
+  # OLD:   merchant_brand = Map.get(response_data, :merchant_brand) || Map.get(response_data, :reg_name) || ""
+  # OLD:   merchant_legal_name = Map.get(response_data, :merchant_legal_name) || merchant_brand || ""
+  # OLD:   merchant_franchise = Map.get(response_data, :merchant_franchise) || ""
+  # OLD:   ownership_type = Map.get(response_data, :ownership_type) || "PRIVATE"
+  # OLD:   invoice_name = Map.get(response_data, :invoice_name) || merchant_brand || ""
+  # OLD:   invoice_number = Map.get(response_data, :invoice_number) || ""
+  # OLD:   invoice_date = Map.get(response_data, :invoice_date) || get_timestamp()
+  # OLD:
+  # OLD:   # Extract merchant identifiers for Identifier tag
+  # OLD:   merchant_sub_code = Map.get(response_data, :sub_code) || Map.get(response_data, :identifier_sub_code, "0000")
+  # OLD:   merchant_mid = extract_mid_from_qr(request_source) || Map.get(response_data, :identifier_mid, "")
+  # OLD:   merchant_sid = extract_msid_from_qr(request_source) || Map.get(response_data, :identifier_sid, "")
+  # OLD:   merchant_tid = extract_mtid_from_qr(request_source) || Map.get(response_data, :identifier_tid, "")
+  # OLD:   merchant_type = extract_merchant_type_from_qr(request_source) || Map.get(response_data, :merchant_type, "SMALL")
+  # OLD:   merchant_genre = extract_merchant_genre_from_qr(request_source) || Map.get(response_data, :merchant_genre, "RETAIL")
+  # OLD:   merchant_onboarding = extract_merchant_onboarding_from_qr(request_source) || Map.get(response_data, :onboarding_type, "AGGREGATOR")
+  # OLD:   merchant_loc = extract_merchant_loc_from_qr(request_source) || Map.get(response_data, :merchant_loc, "IN")
+  # OLD:   merchant_inst_code = Map.get(response_data, :identifier_inst_code) ||
+  # OLD:                         Map.get(response_data, :merchant_inst_code, "")
+  # OLD:
+  # OLD:   # Build Identifier tag only if there's merchant data
+  # OLD:   identifier_section = if merchant_mid != "" || merchant_sid != "" || merchant_tid != "" do
+  # OLD:     "<Identifier subCode=\"#{merchant_sub_code}\" mid=\"#{merchant_mid}\" sid=\"#{merchant_sid}\" tid=\"#{merchant_tid}\" merchantType=\"#{merchant_type}\" merchantGenre=\"#{merchant_genre}\" onBoardingType=\"#{merchant_onboarding}\" merchantLoc=\"#{merchant_loc}\" merchantInstCode=\"#{merchant_inst_code}\"/>"
+  # OLD:   else
+  # OLD:     ""
+  # OLD:   end
+  # OLD:
+  # OLD:   # Build Merchant section
+  # OLD:   merchant_section = if identifier_section != "" || merchant_brand != "" do
+  # OLD:     "<Merchant>" <>
+  # OLD:     identifier_section <>
+  # OLD:     "<Name brand=\"#{merchant_brand}\" legal=\"#{merchant_legal_name}\" franchise=\"#{merchant_franchise}\"/>" <>
+  # OLD:     "<Ownership type=\"#{ownership_type}\"/>" <>
+  # OLD:     "<Invoice name=\"#{invoice_name}\" num=\"#{invoice_number}\" date=\"#{invoice_date}\"/>" <>
+  # OLD:     "</Merchant>"
+  # OLD:   else
+  # OLD:     ""
+  # OLD:   end
+  # OLD:
+  # OLD:   # Build Institution section
+  # OLD:   qr_payload_for_resp = Map.get(request_source, :qr_payload) || Map.get(response_data, :qr_payload) || ""
+  # OLD:   net_inst_id = Map.get(request_source, :net_inst_id) || Map.get(response_data, :net_inst_id, "MER1010001")
+  # OLD:   institution_con_code = extract_country_code_from_qr(qr_payload_for_resp, request_source) || "IN"
+  # OLD:
+  # OLD:   institution_section = if merchant_section != "" || qr_payload_for_resp != "" do
+  # OLD:     "<Institution netInstId=\"#{net_inst_id}\" QrPayLoad=\"#{escape_xml_entities(qr_payload_for_resp)}\" conCode=\"#{institution_con_code}\"/>"
+  # OLD:   else
+  # OLD:     ""
+  # OLD:   end
 
   xml_body = """
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <upi:RespPay xmlns:upi="http://npci.org/upi/schema/">
   <Head ver="2.0" ts="#{Map.get(response_data, :head_ts, get_timestamp())}" orgId="#{Map.get(response_data, :org_id)}" msgId="#{generate_fixed_length_msg_id(Map.get(response_data, :msg_id))}"/>
-  <Txn id="#{txn_id}"
-       note="#{validate_and_preserve_note(Map.get(request_source, :note) || Map.get(response_data, :note))}"
-       refId="#{Map.get(request_source, :ref_id) || Map.get(response_data, :ref_id) || ""}"
-       custRef="#{Map.get(request_source, :cust_ref) || Map.get(response_data, :cust_ref)}"
-       refUrl="#{get_psp_ref_url()}"
-       ts="#{Map.get(request_source, :txn_ts) || Map.get(response_data, :txn_ts, get_timestamp())}"
-       purpose="#{Map.get(request_source, :purpose) || Map.get(response_data, :purpose, "11") }"
-       type="#{Map.get(request_source, :txn_type) || Map.get(response_data, :txn_type)}"
-       subType="#{Map.get(request_source, :sub_type) || Map.get(response_data, :sub_type, "PAY") }"
-       initiationMode="#{Map.get(request_source, :initiation_mode) || Map.get(response_data, :initiation_mode, "01") }"
-       orgTxnId="#{Map.get(request_source, :org_txn_id) || Map.get(response_data, :org_txn_id) || ""}"
-       refCategory="#{Map.get(request_source, :ref_category) || Map.get(response_data, :ref_category, "00") }">
+  <Txn id="#{NpciFormat.xml_attr(txn_id)}"#{attr_always.("note", req_val.(:note))}#{attr_always.("refId", req_val.(:ref_id))}#{attr_always.("custRef", req_val.(:cust_ref))} refUrl="#{get_psp_ref_url()}"#{attr_always.("ts", req_val.(:txn_ts))}#{attr_always.("purpose", req_val.(:purpose))}#{attr_always.("type", req_val.(:txn_type))}#{attr_always.("subType", req_val.(:sub_type))}#{attr_always.("initiationMode", req_val.(:initiation_mode))}#{attr_always.("orgTxnId", req_val.(:org_txn_id))}#{attr_always.("refCategory", req_val.(:ref_category))}>
     <RiskScores>
       <Score provider="NPCI" type="TXNRISK" value="00999"/>
     </RiskScores>
-    <QR expireTs="#{qr_expire_ts}" qVer="#{qr_ver}" qrMedium="#{qr_medium}"#{if qr_ts_attribute, do: " ts=\"#{qr_ts_attribute}\"", else: ""} />
+    <QR#{attr_always.("expireTs", qr_expire_ts)}#{attr_always.("qVer", qr_ver)}#{attr_always.("qrMedium", qr_medium)}#{if qr_ts_attribute not in [nil, ""], do: attr_always.("ts", qr_ts_attribute), else: ""} />
   </Txn>
-  <Resp reqMsgId="#{resp_req_msg_id || ""}" result="#{Map.get(response_data, :result)}"#{if Map.get(response_data, :result) != "SUCCESS", do: " errCode=\"#{err_code}\"", else: ""}>
-    <Ref type="PAYEE" seqNum="1" addr="#{payee_addr}" code="#{Map.get(response_data, :payee_code)}" orgAmount="#{org_amount}" respCode="#{Map.get(response_data, :resp_code) || ""}" regName="#{Map.get(response_data, :reg_name)}" IFSC="#{Map.get(response_data, :ifsc)}" acNum="#{Map.get(response_data, :ac_num)}" accType="#{Map.get(response_data, :acc_type) || ""}" approvalNum="#{approval_num_truncated}" settAmount="#{sett_amount_final}" settCurrency="#{Map.get(response_data, :sett_currency) || Map.get(response_data, "sett_currency") || "INR"}" />
-    #{merchant_section}
-    #{institution_section}
+  <Resp reqMsgId="#{NpciFormat.xml_attr(resp_req_msg_id)}" result="#{NpciFormat.xml_attr(result_final)}"#{if result_final != "SUCCESS", do: attr_always.("errCode", err_code), else: ""}>
+    <Ref type="PAYEE" seqNum="1"#{attr_always.("addr", payee_addr)}#{attr_always.("code", req_val.(:payee_code))} orgAmount="#{NpciFormat.xml_attr(org_amount)}"#{attr_always.("respCode", ref_resp_code)}#{attr_always.("regName", Map.get(request_source, :payee_name) || Map.get(response_data, :reg_name))}#{attr_always.("IFSC", Map.get(request_source, :payee_ifsc) || Map.get(response_data, :ifsc))}#{attr_always.("acNum", Map.get(request_source, :payee_ac_num) || Map.get(response_data, :ac_num))}#{attr_always.("accType", Map.get(request_source, :payee_ac_type) || Map.get(response_data, :acc_type))} approvalNum="#{approval_num_truncated}" settAmount="#{NpciFormat.xml_attr(sett_amount_final)}" settCurrency="INR" />
   </Resp>
 """
 
@@ -2410,178 +2496,182 @@ end
   end
 
 
-  defp extract_location_from_qr_string(qr_payload) do
-    # Parse QR payload to extract merchant location from mLoc= parameter
-    # QR format typically contains mLoc=VALUE in query parameters
-    require Logger
-    Logger.debug("Parsing QR payload for mLoc: #{inspect(qr_payload)}")
-
-    result = cond do
-      # Try standard query parameter format: mLoc=VALUE
-      match = Regex.run(~r/[&?]mLoc=([^&\s]+)/i, qr_payload) ->
-        [_, location] = match
-        loc = String.trim(location)
-        Logger.debug("Found mLoc via query param: #{loc}")
-        loc
-
-      # Try colon separator: mLoc:VALUE
-      match = Regex.run(~r/mLoc:([^:&\s]+)/i, qr_payload) ->
-        [_, location] = match
-        loc = String.trim(location)
-        Logger.debug("Found mLoc via colon: #{loc}")
-        loc
-
-      # Try XML-like format: <mLoc>VALUE</mLoc>
-      match = Regex.run(~r/<mLoc>([^<]+)<\/mLoc>/i, qr_payload) ->
-        [_, location] = match
-        loc = String.trim(location)
-        Logger.debug("Found mLoc via XML: #{loc}")
-        loc
-
-      # Try simple format: mLocVALUE (less common)
-      match = Regex.run(~r/mLoc([A-Za-z0-9]+)/i, qr_payload) ->
-        [_, location] = match
-        loc = String.trim(location)
-        Logger.debug("Found mLoc via simple: #{loc}")
-        loc
-
-      # Default fallback
-      true ->
-        Logger.debug("No mLoc found in QR payload, using default: International")
-        "International"
-    end
-
-    Logger.debug("Final extracted mLoc: #{result}")
-    result
-  end
-
-  defp extract_mid_from_qr(data) do
-    # Extract merchant ID (mid) from QR payload
-    # QR format typically contains mid=VALUE in query parameters
-    require Logger
-
-    qr_payload = case data do
-      %{qr_payload: payload} when is_binary(payload) -> payload
-      %{qr_string: payload} when is_binary(payload) -> payload
-      _ -> ""
-    end
-
-    Logger.debug("Parsing QR payload for mid: #{inspect(qr_payload)}")
-
-    result = cond do
-      # Try standard query parameter format: mid=VALUE (handles both & and &amp;)
-      match = Regex.run(~r/(?:[&?]|&amp;)mid=([^&\s]+)/i, qr_payload) ->
-        [_, mid] = match
-        merchant_id = URI.decode(String.trim(mid))
-        Logger.debug("Found mid via query param: #{merchant_id}")
-        merchant_id
-
-      # Try colon separator: mid:VALUE
-      match = Regex.run(~r/mid:([^:&\s]+)/i, qr_payload) ->
-        [_, mid] = match
-        merchant_id = URI.decode(String.trim(mid))
-        Logger.debug("Found mid via colon: #{merchant_id}")
-        merchant_id
-
-      # Try XML-like format: <mid>VALUE</mid>
-      match = Regex.run(~r/<mid>([^<]+)<\/mid>/i, qr_payload) ->
-        [_, mid] = match
-        merchant_id = URI.decode(String.trim(mid))
-        Logger.debug("Found mid via XML: #{merchant_id}")
-        merchant_id
-
-      # Default fallback - return nil so we can use database value or default
-      true ->
-        Logger.debug("No mid found in QR payload")
-        nil
-    end
-
-    Logger.debug("Final extracted mid: #{result}")
-    result
-  end
-
-  defp extract_mtid_from_qr(data) do
-    # Extract terminal ID (mtid) from QR payload for mode 17 (mandate)
-    # QR format typically contains mtid=VALUE in query parameters
-    require Logger
-
-    qr_payload = case data do
-      %{qr_payload: payload} when is_binary(payload) -> payload
-      %{qr_string: payload} when is_binary(payload) -> payload
-      _ -> ""
-    end
-
-    Logger.debug("Parsing QR payload for mtid: #{inspect(qr_payload)}")
-
-    result = cond do
-      # Try standard query parameter format: mtid=VALUE (handles both & and &amp;)
-      match = Regex.run(~r/(?:[&?]|&amp;)mtid=([^&\s]+)/i, qr_payload) ->
-        [_, mtid] = match
-        tid = URI.decode(String.trim(mtid))
-        Logger.debug("Found mtid via query param: #{tid}")
-        tid
-
-      # Try colon separator: mtid:VALUE
-      match = Regex.run(~r/mtid:([^:&\s]+)/i, qr_payload) ->
-        [_, mtid] = match
-        tid = URI.decode(String.trim(mtid))
-        Logger.debug("Found mtid via colon: #{tid}")
-        tid
-
-      # Default fallback - return nil so we can use database value or default
-      true ->
-        Logger.debug("No mtid found in QR payload")
-        nil
-    end
-
-    Logger.debug("Final extracted mtid: #{result}")
-    result
-  end
-
-  defp extract_msid_from_qr(data) do
-    # Extract store ID (msid) from QR payload for merchant transactions
-    # QR format typically contains msid=VALUE in query parameters
-    require Logger
-
-    qr_payload = case data do
-      %{qr_payload: payload} when is_binary(payload) -> payload
-      %{qr_string: payload} when is_binary(payload) -> payload
-      _ -> ""
-    end
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_location_from_qr_string is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   defp extract_location_from_qr_string(qr_payload) do
+  # OLD:     # Parse QR payload to extract merchant location from mLoc= parameter
+  # OLD:     # QR format typically contains mLoc=VALUE in query parameters
+  # OLD:     require Logger
+  # OLD:     Logger.debug("Parsing QR payload for mLoc: #{inspect(qr_payload)}")
+  # OLD:
+  # OLD:     result = cond do
+  # OLD:       # Try standard query parameter format: mLoc=VALUE
+  # OLD:       match = Regex.run(~r/[&?]mLoc=([^&\s]+)/i, qr_payload) ->
+  # OLD:         [_, location] = match
+  # OLD:         loc = String.trim(location)
+  # OLD:         Logger.debug("Found mLoc via query param: #{loc}")
+  # OLD:         loc
+  # OLD:
+  # OLD:       # Try colon separator: mLoc:VALUE
+  # OLD:       match = Regex.run(~r/mLoc:([^:&\s]+)/i, qr_payload) ->
+  # OLD:         [_, location] = match
+  # OLD:         loc = String.trim(location)
+  # OLD:         Logger.debug("Found mLoc via colon: #{loc}")
+  # OLD:         loc
+  # OLD:
+  # OLD:       # Try XML-like format: <mLoc>VALUE</mLoc>
+  # OLD:       match = Regex.run(~r/<mLoc>([^<]+)<\/mLoc>/i, qr_payload) ->
+  # OLD:         [_, location] = match
+  # OLD:         loc = String.trim(location)
+  # OLD:         Logger.debug("Found mLoc via XML: #{loc}")
+  # OLD:         loc
+  # OLD:
+  # OLD:       # Try simple format: mLocVALUE (less common)
+  # OLD:       match = Regex.run(~r/mLoc([A-Za-z0-9]+)/i, qr_payload) ->
+  # OLD:         [_, location] = match
+  # OLD:         loc = String.trim(location)
+  # OLD:         Logger.debug("Found mLoc via simple: #{loc}")
+  # OLD:         loc
+  # OLD:
+  # OLD:       # Default fallback
+  # OLD:       true ->
+  # OLD:         Logger.debug("No mLoc found in QR payload, using default: International")
+  # OLD:         "International"
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Final extracted mLoc: #{result}")
+  # OLD:     result
+  # OLD:   end
 
-    Logger.debug("Parsing QR payload for msid: #{inspect(qr_payload)}")
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_mid_from_qr is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   defp extract_mid_from_qr(data) do
+  # OLD:     # Extract merchant ID (mid) from QR payload
+  # OLD:     # QR format typically contains mid=VALUE in query parameters
+  # OLD:     require Logger
+  # OLD:
+  # OLD:     qr_payload = case data do
+  # OLD:       %{qr_payload: payload} when is_binary(payload) -> payload
+  # OLD:       %{qr_string: payload} when is_binary(payload) -> payload
+  # OLD:       _ -> ""
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Parsing QR payload for mid: #{inspect(qr_payload)}")
+  # OLD:
+  # OLD:     result = cond do
+  # OLD:       # Try standard query parameter format: mid=VALUE (handles both & and &amp;)
+  # OLD:       match = Regex.run(~r/(?:[&?]|&amp;)mid=([^&\s]+)/i, qr_payload) ->
+  # OLD:         [_, mid] = match
+  # OLD:         merchant_id = URI.decode(String.trim(mid))
+  # OLD:         Logger.debug("Found mid via query param: #{merchant_id}")
+  # OLD:         merchant_id
+  # OLD:
+  # OLD:       # Try colon separator: mid:VALUE
+  # OLD:       match = Regex.run(~r/mid:([^:&\s]+)/i, qr_payload) ->
+  # OLD:         [_, mid] = match
+  # OLD:         merchant_id = URI.decode(String.trim(mid))
+  # OLD:         Logger.debug("Found mid via colon: #{merchant_id}")
+  # OLD:         merchant_id
+  # OLD:
+  # OLD:       # Try XML-like format: <mid>VALUE</mid>
+  # OLD:       match = Regex.run(~r/<mid>([^<]+)<\/mid>/i, qr_payload) ->
+  # OLD:         [_, mid] = match
+  # OLD:         merchant_id = URI.decode(String.trim(mid))
+  # OLD:         Logger.debug("Found mid via XML: #{merchant_id}")
+  # OLD:         merchant_id
+  # OLD:
+  # OLD:       # Default fallback - return nil so we can use database value or default
+  # OLD:       true ->
+  # OLD:         Logger.debug("No mid found in QR payload")
+  # OLD:         nil
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Final extracted mid: #{result}")
+  # OLD:     result
+  # OLD:   end
 
-    result = cond do
-      # Try standard query parameter format: msid=VALUE (handles both & and &amp;)
-      match = Regex.run(~r/(?:[&?]|&amp;)msid=([^&\s]+)/i, qr_payload) ->
-        [_, msid] = match
-        sid = URI.decode(String.trim(msid))
-        Logger.debug("Found msid via query param: #{sid}")
-        sid
-
-      # Try colon separator: msid:VALUE
-      match = Regex.run(~r/msid:([^:&\s]+)/i, qr_payload) ->
-        [_, msid] = match
-        sid = URI.decode(String.trim(msid))
-        Logger.debug("Found msid via colon: #{sid}")
-        sid
-
-      # Try sid parameter as alternative (handles both & and &amp;)
-      match = Regex.run(~r/(?:[&?]|&amp;)sid=([^&\s]+)/i, qr_payload) ->
-        [_, sid] = match
-        store_id = URI.decode(String.trim(sid))
-        Logger.debug("Found sid via query param: #{store_id}")
-        store_id
-
-      # Default fallback - return nil so we can use database value or default
-      true ->
-        Logger.debug("No msid found in QR payload")
-        nil
-    end
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_mtid_from_qr is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   defp extract_mtid_from_qr(data) do
+  # OLD:     # Extract terminal ID (mtid) from QR payload for mode 17 (mandate)
+  # OLD:     # QR format typically contains mtid=VALUE in query parameters
+  # OLD:     require Logger
+  # OLD:
+  # OLD:     qr_payload = case data do
+  # OLD:       %{qr_payload: payload} when is_binary(payload) -> payload
+  # OLD:       %{qr_string: payload} when is_binary(payload) -> payload
+  # OLD:       _ -> ""
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Parsing QR payload for mtid: #{inspect(qr_payload)}")
+  # OLD:
+  # OLD:     result = cond do
+  # OLD:       # Try standard query parameter format: mtid=VALUE (handles both & and &amp;)
+  # OLD:       match = Regex.run(~r/(?:[&?]|&amp;)mtid=([^&\s]+)/i, qr_payload) ->
+  # OLD:         [_, mtid] = match
+  # OLD:         tid = URI.decode(String.trim(mtid))
+  # OLD:         Logger.debug("Found mtid via query param: #{tid}")
+  # OLD:         tid
+  # OLD:
+  # OLD:       # Try colon separator: mtid:VALUE
+  # OLD:       match = Regex.run(~r/mtid:([^:&\s]+)/i, qr_payload) ->
+  # OLD:         [_, mtid] = match
+  # OLD:         tid = URI.decode(String.trim(mtid))
+  # OLD:         Logger.debug("Found mtid via colon: #{tid}")
+  # OLD:         tid
+  # OLD:
+  # OLD:       # Default fallback - return nil so we can use database value or default
+  # OLD:       true ->
+  # OLD:         Logger.debug("No mtid found in QR payload")
+  # OLD:         nil
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Final extracted mtid: #{result}")
+  # OLD:     result
+  # OLD:   end
 
-    Logger.debug("Final extracted msid: #{result}")
-    result
-  end
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_msid_from_qr is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   defp extract_msid_from_qr(data) do
+  # OLD:     # Extract store ID (msid) from QR payload for merchant transactions
+  # OLD:     # QR format typically contains msid=VALUE in query parameters
+  # OLD:     require Logger
+  # OLD:
+  # OLD:     qr_payload = case data do
+  # OLD:       %{qr_payload: payload} when is_binary(payload) -> payload
+  # OLD:       %{qr_string: payload} when is_binary(payload) -> payload
+  # OLD:       _ -> ""
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Parsing QR payload for msid: #{inspect(qr_payload)}")
+  # OLD:
+  # OLD:     result = cond do
+  # OLD:       # Try standard query parameter format: msid=VALUE (handles both & and &amp;)
+  # OLD:       match = Regex.run(~r/(?:[&?]|&amp;)msid=([^&\s]+)/i, qr_payload) ->
+  # OLD:         [_, msid] = match
+  # OLD:         sid = URI.decode(String.trim(msid))
+  # OLD:         Logger.debug("Found msid via query param: #{sid}")
+  # OLD:         sid
+  # OLD:
+  # OLD:       # Try colon separator: msid:VALUE
+  # OLD:       match = Regex.run(~r/msid:([^:&\s]+)/i, qr_payload) ->
+  # OLD:         [_, msid] = match
+  # OLD:         sid = URI.decode(String.trim(msid))
+  # OLD:         Logger.debug("Found msid via colon: #{sid}")
+  # OLD:         sid
+  # OLD:
+  # OLD:       # Try sid parameter as alternative (handles both & and &amp;)
+  # OLD:       match = Regex.run(~r/(?:[&?]|&amp;)sid=([^&\s]+)/i, qr_payload) ->
+  # OLD:         [_, sid] = match
+  # OLD:         store_id = URI.decode(String.trim(sid))
+  # OLD:         Logger.debug("Found sid via query param: #{store_id}")
+  # OLD:         store_id
+  # OLD:
+  # OLD:       # Default fallback - return nil so we can use database value or default
+  # OLD:       true ->
+  # OLD:         Logger.debug("No msid found in QR payload")
+  # OLD:         nil
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Final extracted msid: #{result}")
+  # OLD:     result
+  # OLD:   end
 
   # [RespValQr Merchant.Invoice num] REMOVED: extract_invoice_number_from_qr is unused now - invoiceNo is read with NpciFormat.qr_param
   # OLD:   defp extract_invoice_number_from_qr(data) do
@@ -2729,31 +2819,32 @@ end
   # OLD:     result
   # OLD:   end
 
-  defp extract_merchant_location_from_qr(data) do
-    # Extract merchant location from QR payload or other data sources
-    # This follows NPCI merchant location standards
-    cond do
-      # If merchant_loc is already provided in data
-      data[:merchant_loc] && data[:merchant_loc] != "" ->
-        data[:merchant_loc]
-
-      # Try to derive from QR payload if present
-      data[:qr_payload] && data[:qr_payload] != "" ->
-        extract_location_from_qr_string(data[:qr_payload])
-
-      # Try alternative QR field names
-      data[:qr_string] && data[:qr_string] != "" ->
-        extract_location_from_qr_string(data[:qr_string])
-
-      # Derive from country code
-      data[:con_code] ->
-        derive_location_from_country_code(data[:con_code])
-
-      # Default for international transactions
-      true ->
-        "IN"
-    end
-  end
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_merchant_location_from_qr is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   defp extract_merchant_location_from_qr(data) do
+  # OLD:     # Extract merchant location from QR payload or other data sources
+  # OLD:     # This follows NPCI merchant location standards
+  # OLD:     cond do
+  # OLD:       # If merchant_loc is already provided in data
+  # OLD:       data[:merchant_loc] && data[:merchant_loc] != "" ->
+  # OLD:         data[:merchant_loc]
+  # OLD:
+  # OLD:       # Try to derive from QR payload if present
+  # OLD:       data[:qr_payload] && data[:qr_payload] != "" ->
+  # OLD:         extract_location_from_qr_string(data[:qr_payload])
+  # OLD:
+  # OLD:       # Try alternative QR field names
+  # OLD:       data[:qr_string] && data[:qr_string] != "" ->
+  # OLD:         extract_location_from_qr_string(data[:qr_string])
+  # OLD:
+  # OLD:       # Derive from country code
+  # OLD:       data[:con_code] ->
+  # OLD:         derive_location_from_country_code(data[:con_code])
+  # OLD:
+  # OLD:       # Default for international transactions
+  # OLD:       true ->
+  # OLD:         "IN"
+  # OLD:     end
+  # OLD:   end
 
   defp derive_location_from_country_code(con_code) do
     # Map country code to location as per NPCI standards
@@ -2769,86 +2860,91 @@ end
     end
   end
 
-@doc """
-Matches NPCI expected formatting (attributes inline, consistent indentation).
-"""
-def generate_credit_response(response_data) do
-  xml_content = """
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<upi:RespPay xmlns:upi="http://npci.org/upi/schema/">
-    <Head msgId="#{generate_fixed_length_msg_id(Map.get(response_data, :msg_id))}"
-          orgId="#{"MER101"}"
-          prodType="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :prod_type, "UPI")))}"
-          ts="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :head_ts, get_timestamp())))}"
-          ver="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ver, "2.0")))}"/>
-    <Txn custRef="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :cust_ref, "")))}"
-         id="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :txn_id, "")))}"
-         initiationMode="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :initiation_mode, "01")))}"
-         note="#{escape_xml_entities(validate_and_preserve_note(Map.get(response_data, :note)))}"
-         orgTxnId="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :org_txn_id, "")))}"
-         purpose="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :purpose, "11")))}"
-         refCategory="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ref_category, "00")))}"
-         refId="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ref_id, "")))}"
-         refUrl="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ref_url, "")))}"
-         seqNum="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :seq_num, generate_numeric_seq_num())))}"
-         subType="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :sub_type, "PAY")))}"
-         ts="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :txn_ts, get_timestamp())))}"
-         type="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :txn_type, "CREDIT")))}">
-        <approvalNum>#{escape_xml_entities(sanitize_attr(Map.get(response_data, :approval_num, generate_approval_num())))}</approvalNum>
-#{generate_risk_scores_xml(Map.get(response_data, :risk_scores))}
-#{generate_qr_xml(Map.get(response_data, :qr_data))}
-    </Txn>
-    <Ref IFSC="#{sanitize_attr(response_data.payee_ref.ifsc)}"
-       acNum="#{sanitize_attr(response_data.payee_ref.ac_num)}"
-       accType="#{sanitize_attr(response_data.payee_ref.acc_type)}"
-       addr="#{sanitize_attr(response_data.payee_ref.addr)}"
-       code="#{sanitize_attr(response_data.payee_ref.code)}"
-       orgAmount="#{sanitize_attr(response_data.payee_ref.org_amount)}"
-       regName="#{sanitize_attr(response_data.payee_ref.reg_name)}"
-       respCode="#{sanitize_attr(response_data.payee_ref.resp_code)}"
-       seqNum="#{sanitize_attr(response_data.payee_ref.seq_num)}"
-       settAmount="#{sanitize_attr(response_data.payee_ref.sett_amount)}"
-       settCurrency="#{sanitize_attr(response_data.payee_ref.sett_currency)}"
-       type="#{sanitize_attr(response_data.payee_ref.type)}"/>
-    </Resp>
-"""
-
-
-  # Generate the Signature block
-  signature_block = generate_signature_block(xml_content)
-
-  # # Combine XML content with signature block and closing tag
-   complete_xml = xml_content <> signature_block <> "\n</upi:RespPay>"
-  {:ok, complete_xml}
-end
-
-  # Helper to sanitize attribute values: trims whitespace and converts nil to empty string
-  defp sanitize_attr(nil), do: ""
-  defp sanitize_attr(value) when is_binary(value), do: String.trim(value)
-  defp sanitize_attr(value), do: value |> to_string() |> String.trim()
-
-  # Generate RiskScores section
-  defp generate_risk_scores_xml(risk_scores) when is_map(risk_scores) do
-  """
-    <RiskScores>
-      <Score provider="NPCI" type="TXNRISK" value="#{risk_scores["value"] || "00030"}"/>
-    </RiskScores>
-  """
-  end
-
-  defp generate_risk_scores_xml(_), do: ""
-
-  defp generate_qr_xml(qr_data) when is_map(qr_data) do
-    """
-        <QR expireTs="#{Map.get(qr_data, :expire_ts, "")}"
-           qVer="#{Map.get(qr_data, :ver, "")}"
-           qrMedium="#{Map.get(qr_data, :medium, "")}"
-           stan="#{Map.get(qr_data, :stan, "")}"
-           ts="#{Map.get(qr_data, :ts, "")}"/>
-    """
-  end
-
-  defp generate_qr_xml(_), do: ""
+# [RespPay generate_credit_response] REMOVED: replaced by generate_resp_pay (callers moved). The old builder had no <Resp> open tag, took Ref values from a made-up payee_ref map (8999 / MSWI0000000 / CURRENT defaults) and generated custRef / seqNum / approvalNum / timestamps
+# TSD Table 4: RespPay <Resp> carries only <Ref>, built from ReqPay
+# OLD: @doc """
+# OLD: Matches NPCI expected formatting (attributes inline, consistent indentation).
+# OLD: """
+# OLD: def generate_credit_response(response_data) do
+# OLD:   xml_content = """
+# OLD: <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+# OLD: <upi:RespPay xmlns:upi="http://npci.org/upi/schema/">
+# OLD:     <Head msgId="#{generate_fixed_length_msg_id(Map.get(response_data, :msg_id))}"
+# OLD:           orgId="#{"MER101"}"
+# OLD:           prodType="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :prod_type, "UPI")))}"
+# OLD:           ts="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :head_ts, get_timestamp())))}"
+# OLD:           ver="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ver, "2.0")))}"/>
+# OLD:     <Txn custRef="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :cust_ref, "")))}"
+# OLD:          id="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :txn_id, "")))}"
+# OLD:          initiationMode="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :initiation_mode, "01")))}"
+# OLD:          note="#{escape_xml_entities(validate_and_preserve_note(Map.get(response_data, :note)))}"
+# OLD:          orgTxnId="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :org_txn_id, "")))}"
+# OLD:          purpose="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :purpose, "11")))}"
+# OLD:          refCategory="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ref_category, "00")))}"
+# OLD:          refId="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ref_id, "")))}"
+# OLD:          refUrl="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :ref_url, "")))}"
+# OLD:          seqNum="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :seq_num, generate_numeric_seq_num())))}"
+# OLD:          subType="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :sub_type, "PAY")))}"
+# OLD:          ts="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :txn_ts, get_timestamp())))}"
+# OLD:          type="#{escape_xml_entities(sanitize_attr(Map.get(response_data, :txn_type, "CREDIT")))}">
+# OLD:         <approvalNum>#{escape_xml_entities(sanitize_attr(Map.get(response_data, :approval_num, generate_approval_num())))}</approvalNum>
+# OLD: #{generate_risk_scores_xml(Map.get(response_data, :risk_scores))}
+# OLD: #{generate_qr_xml(Map.get(response_data, :qr_data))}
+# OLD:     </Txn>
+# OLD:     <Ref IFSC="#{sanitize_attr(response_data.payee_ref.ifsc)}"
+# OLD:        acNum="#{sanitize_attr(response_data.payee_ref.ac_num)}"
+# OLD:        accType="#{sanitize_attr(response_data.payee_ref.acc_type)}"
+# OLD:        addr="#{sanitize_attr(response_data.payee_ref.addr)}"
+# OLD:        code="#{sanitize_attr(response_data.payee_ref.code)}"
+# OLD:        orgAmount="#{sanitize_attr(response_data.payee_ref.org_amount)}"
+# OLD:        regName="#{sanitize_attr(response_data.payee_ref.reg_name)}"
+# OLD:        respCode="#{sanitize_attr(response_data.payee_ref.resp_code)}"
+# OLD:        seqNum="#{sanitize_attr(response_data.payee_ref.seq_num)}"
+# OLD:        settAmount="#{sanitize_attr(response_data.payee_ref.sett_amount)}"
+# OLD:        settCurrency="#{sanitize_attr(response_data.payee_ref.sett_currency)}"
+# OLD:        type="#{sanitize_attr(response_data.payee_ref.type)}"/>
+# OLD:     </Resp>
+# OLD: """
+# OLD:
+# OLD:
+# OLD:   # Generate the Signature block
+# OLD:   signature_block = generate_signature_block(xml_content)
+# OLD:
+# OLD:   # # Combine XML content with signature block and closing tag
+# OLD:    complete_xml = xml_content <> signature_block <> "\n</upi:RespPay>"
+# OLD:   {:ok, complete_xml}
+# OLD: end
+
+  # [RespPay sanitize_attr] REMOVED: only generate_credit_response used it
+  # OLD:   # Helper to sanitize attribute values: trims whitespace and converts nil to empty string
+  # OLD:   defp sanitize_attr(nil), do: ""
+  # OLD:   defp sanitize_attr(value) when is_binary(value), do: String.trim(value)
+  # OLD:   defp sanitize_attr(value), do: value |> to_string() |> String.trim()
+
+  # [RespPay generate_risk_scores_xml] REMOVED: only generate_credit_response used it (generate_resp_pay writes RiskScores 00999 itself)
+  # OLD:   # Generate RiskScores section
+  # OLD:   defp generate_risk_scores_xml(risk_scores) when is_map(risk_scores) do
+  # OLD:   """
+  # OLD:     <RiskScores>
+  # OLD:       <Score provider="NPCI" type="TXNRISK" value="#{risk_scores["value"] || "00030"}"/>
+  # OLD:     </RiskScores>
+  # OLD:   """
+  # OLD:   end
+  # OLD:
+  # OLD:   defp generate_risk_scores_xml(_), do: ""
+
+  # [RespPay generate_qr_xml] REMOVED: only generate_credit_response used it (it wrote QR stan, an invented value)
+  # OLD:   defp generate_qr_xml(qr_data) when is_map(qr_data) do
+  # OLD:     """
+  # OLD:         <QR expireTs="#{Map.get(qr_data, :expire_ts, "")}"
+  # OLD:            qVer="#{Map.get(qr_data, :ver, "")}"
+  # OLD:            qrMedium="#{Map.get(qr_data, :medium, "")}"
+  # OLD:            stan="#{Map.get(qr_data, :stan, "")}"
+  # OLD:            ts="#{Map.get(qr_data, :ts, "")}"/>
+  # OLD:     """
+  # OLD:   end
+  # OLD:
+  # OLD:   defp generate_qr_xml(_), do: ""
 
   @doc """
   Get UPI error code description
@@ -2871,16 +2967,17 @@ end
   end
 
   # Helper function to escape XML entities in QR payload
-  defp escape_xml_entities(text) when is_binary(text) do
-    text
-    |> String.replace("&", "&amp;")
-    |> String.replace("<", "&lt;")
-    |> String.replace(">", "&gt;")
-    |> String.replace("\"", "&quot;")
-    |> String.replace("'", "&apos;")
-  end
-  defp escape_xml_entities(nil), do: ""
-  defp escape_xml_entities(other), do: to_string(other)
+  # [RespPay <Merchant>/<Institution>] REMOVED: escape_xml_entities (it escaped the Institution QrPayLoad; attributes now use NpciFormat.xml_attr) is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   defp escape_xml_entities(text) when is_binary(text) do
+  # OLD:     text
+  # OLD:     |> String.replace("&", "&amp;")
+  # OLD:     |> String.replace("<", "&lt;")
+  # OLD:     |> String.replace(">", "&gt;")
+  # OLD:     |> String.replace("\"", "&quot;")
+  # OLD:     |> String.replace("'", "&apos;")
+  # OLD:   end
+  # OLD:   defp escape_xml_entities(nil), do: ""
+  # OLD:   defp escape_xml_entities(other), do: to_string(other)
 
   # Helper function to generate a fixed length message ID (NPCI requires 35 chars)
   # Ensures a valid MSGID is always returned: if input is missing or invalid we generate
@@ -3015,20 +3112,21 @@ end
 
   # T03: Validate and preserve TXN.NOTE - must be alphanumeric, length 1-50
   # CRITICAL: Preserve original note value as-is to avoid NPCI T03 validation errors
-  defp validate_and_preserve_note(nil), do: "PAYMENT"
-  defp validate_and_preserve_note(""), do: "PAYMENT"
-  defp validate_and_preserve_note(note) when is_binary(note) do
-    # Validate that note meets NPCI T03 requirements:
-    # 1. Must be alphanumeric (including underscores which are common in UPI notes)
-    # 2. Length between 1-50 characters
-    # 3. Preserve original value exactly as received from ReqPay
-
-    case String.match?(note, ~r/^[a-zA-Z0-9_]+$/) && String.length(note) >= 1 && String.length(note) <= 50 do
-      true -> note  # Use original note exactly as received
-      false -> "PAYMENT"  # Fallback if validation fails
-    end
-  end
-  defp validate_and_preserve_note(_), do: "PAYMENT"
+  # [RespPay Txn.note] REMOVED: validate_and_preserve_note (turned . - and spaces into PAYMENT) is unused now - note is echoed from the request
+  # OLD:   defp validate_and_preserve_note(nil), do: "PAYMENT"
+  # OLD:   defp validate_and_preserve_note(""), do: "PAYMENT"
+  # OLD:   defp validate_and_preserve_note(note) when is_binary(note) do
+  # OLD:     # Validate that note meets NPCI T03 requirements:
+  # OLD:     # 1. Must be alphanumeric (including underscores which are common in UPI notes)
+  # OLD:     # 2. Length between 1-50 characters
+  # OLD:     # 3. Preserve original value exactly as received from ReqPay
+  # OLD:
+  # OLD:     case String.match?(note, ~r/^[a-zA-Z0-9_]+$/) && String.length(note) >= 1 && String.length(note) <= 50 do
+  # OLD:       true -> note  # Use original note exactly as received
+  # OLD:       false -> "PAYMENT"  # Fallback if validation fails
+  # OLD:     end
+  # OLD:   end
+  # OLD:   defp validate_and_preserve_note(_), do: "PAYMENT"
 
   # [RespValQr Txn.custRef] REMOVED: generate_12_char_custref is unused now - no generated custRef
   # OLD:   defp generate_12_char_custref do
@@ -3066,24 +3164,28 @@ end
   defp generate_default_risk_score do
     "00030"
   end
-  defp generate_default_qr_expire_ts do
-    # Generate QR expiry timestamp (24 hours from now) in IST format
-    Timex.now("Asia/Kolkata")
-    |> Timex.add(Timex.Duration.from_hours(24))
-    |> Timex.format!("{YYYY}-{0M}-{0D}T{h24}:{m}:{s}+05:30")
-  end
+  # [RespPay QR / Txn fallbacks] REMOVED: generate_default_qr_expire_ts (now+5min) is unused now - QR and Txn values are echoed from ReqPay, nothing is generated
+  # OLD:   defp generate_default_qr_expire_ts do
+  # OLD:     # Generate QR expiry timestamp (24 hours from now) in IST format
+  # OLD:     Timex.now("Asia/Kolkata")
+  # OLD:     |> Timex.add(Timex.Duration.from_hours(24))
+  # OLD:     |> Timex.format!("{YYYY}-{0M}-{0D}T{h24}:{m}:{s}+05:30")
+  # OLD:   end
 
-  defp generate_default_qr_ver do
-    "02"
-  end
+  # [RespPay QR / Txn fallbacks] REMOVED: generate_default_qr_ver is unused now - QR and Txn values are echoed from ReqPay, nothing is generated
+  # OLD:   defp generate_default_qr_ver do
+  # OLD:     "02"
+  # OLD:   end
 
-  defp generate_default_qr_medium do
-    "03"
-  end
+  # [RespPay QR / Txn fallbacks] REMOVED: generate_default_qr_medium is unused now - QR and Txn values are echoed from ReqPay, nothing is generated
+  # OLD:   defp generate_default_qr_medium do
+  # OLD:     "03"
+  # OLD:   end
 
-  defp generate_default_qr_query do
-    ""
-  end
+  # [RespPay QR / Txn fallbacks] REMOVED: generate_default_qr_query is unused now - QR and Txn values are echoed from ReqPay, nothing is generated
+  # OLD:   defp generate_default_qr_query do
+  # OLD:     ""
+  # OLD:   end
 
   # Ensure message/transaction ids conform to NPCI constraints: non-empty, trimmed, max length 35
   defp sanitize_msg_id(id) when is_binary(id) do
@@ -3206,65 +3308,67 @@ end
     end
   end
 
-  @doc """
-  Extract merchant location from QR payload
-  """
-  defp extract_merchant_loc_from_qr(data) do
-    qr_payload = case data do
-      %{qr_payload: payload} when is_binary(payload) -> payload
-      %{qr_string: payload} when is_binary(payload) -> payload
-      _ -> ""
-    end
-
-    case extract_location_from_qr_string(qr_payload) do
-      nil -> "IN"
-      loc when is_binary(loc) and loc != "" -> loc
-      _ -> "IN"
-    end
-  end
-
-  @doc """
-  Extract merchant onboarding type from QR payload
-  """
-  defp extract_merchant_onboarding_from_qr(data) do
-    require Logger
-    qr_payload = case data do
-      %{qr_payload: payload} when is_binary(payload) -> payload
-      %{qr_string: payload} when is_binary(payload) -> payload
-      _ -> ""
-    end
-
-    Logger.debug("Parsing QR payload for mOnboarding: #{inspect(qr_payload)}")
-
-    result = cond do
-      # Try standard query parameter format: mOnboarding=VALUE
-      match = Regex.run(~r/[&?]mOnboarding=([^&\s]+)/i, qr_payload) ->
-        [_, onboarding] = match
-        onboarding_type = URI.decode(String.trim(onboarding))
-        Logger.debug("Found mOnboarding via query param: #{onboarding_type}")
-        onboarding_type
-
-      # Try colon separator: mOnboarding:VALUE
-      match = Regex.run(~r/mOnboarding:([^:&\s]+)/i, qr_payload) ->
-        [_, onboarding] = match
-        onboarding_type = URI.decode(String.trim(onboarding))
-        Logger.debug("Found mOnboarding via colon: #{onboarding_type}")
-        onboarding_type
-
-      # Try XML-like format: <mOnboarding>VALUE</mOnboarding>
-      match = Regex.run(~r/<mOnboarding>([^<]+)<\/mOnboarding>/i, qr_payload) ->
-        [_, onboarding] = match
-        onboarding_type = URI.decode(String.trim(onboarding))
-        Logger.debug("Found mOnboarding via XML: #{onboarding_type}")
-        onboarding_type
-
-      true ->
-        Logger.debug("mOnboarding not found in QR payload")
-        nil
-    end
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_merchant_loc_from_qr is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   @doc """
+  # OLD:   Extract merchant location from QR payload
+  # OLD:   """
+  # OLD:   defp extract_merchant_loc_from_qr(data) do
+  # OLD:     qr_payload = case data do
+  # OLD:       %{qr_payload: payload} when is_binary(payload) -> payload
+  # OLD:       %{qr_string: payload} when is_binary(payload) -> payload
+  # OLD:       _ -> ""
+  # OLD:     end
+  # OLD:
+  # OLD:     case extract_location_from_qr_string(qr_payload) do
+  # OLD:       nil -> "IN"
+  # OLD:       loc when is_binary(loc) and loc != "" -> loc
+  # OLD:       _ -> "IN"
+  # OLD:     end
+  # OLD:   end
 
-    result
-  end
+  # [RespPay <Merchant>/<Institution>] REMOVED: extract_merchant_onboarding_from_qr is unused now - RespPay <Resp> has only <Ref> (TSD Table 4)
+  # OLD:   @doc """
+  # OLD:   Extract merchant onboarding type from QR payload
+  # OLD:   """
+  # OLD:   defp extract_merchant_onboarding_from_qr(data) do
+  # OLD:     require Logger
+  # OLD:     qr_payload = case data do
+  # OLD:       %{qr_payload: payload} when is_binary(payload) -> payload
+  # OLD:       %{qr_string: payload} when is_binary(payload) -> payload
+  # OLD:       _ -> ""
+  # OLD:     end
+  # OLD:
+  # OLD:     Logger.debug("Parsing QR payload for mOnboarding: #{inspect(qr_payload)}")
+  # OLD:
+  # OLD:     result = cond do
+  # OLD:       # Try standard query parameter format: mOnboarding=VALUE
+  # OLD:       match = Regex.run(~r/[&?]mOnboarding=([^&\s]+)/i, qr_payload) ->
+  # OLD:         [_, onboarding] = match
+  # OLD:         onboarding_type = URI.decode(String.trim(onboarding))
+  # OLD:         Logger.debug("Found mOnboarding via query param: #{onboarding_type}")
+  # OLD:         onboarding_type
+  # OLD:
+  # OLD:       # Try colon separator: mOnboarding:VALUE
+  # OLD:       match = Regex.run(~r/mOnboarding:([^:&\s]+)/i, qr_payload) ->
+  # OLD:         [_, onboarding] = match
+  # OLD:         onboarding_type = URI.decode(String.trim(onboarding))
+  # OLD:         Logger.debug("Found mOnboarding via colon: #{onboarding_type}")
+  # OLD:         onboarding_type
+  # OLD:
+  # OLD:       # Try XML-like format: <mOnboarding>VALUE</mOnboarding>
+  # OLD:       match = Regex.run(~r/<mOnboarding>([^<]+)<\/mOnboarding>/i, qr_payload) ->
+  # OLD:         [_, onboarding] = match
+  # OLD:         onboarding_type = URI.decode(String.trim(onboarding))
+  # OLD:         Logger.debug("Found mOnboarding via XML: #{onboarding_type}")
+  # OLD:         onboarding_type
+  # OLD:
+  # OLD:       true ->
+  # OLD:         Logger.debug("mOnboarding not found in QR payload")
+  # OLD:         nil
+  # OLD:     end
+  # OLD:
+  # OLD:     result
+  # OLD:   end
 
   @doc """
   Generate approval number for transactions
diff --git a/apps/upi_core/test/upi_core/resp_pay_test.exs b/apps/upi_core/test/upi_core/resp_pay_test.exs
new file mode 100644
index 0000000..087588c
--- /dev/null
+++ b/apps/upi_core/test/upi_core/resp_pay_test.exs
@@ -0,0 +1,251 @@
+defmodule UpiCore.RespPayTest do
+  use ExUnit.Case, async: false
+
+  import SweetXml
+
+  alias UpiCore.UpiXmlSchema
+
+  @key_path Path.expand("../../../../private.pem", __DIR__)
+
+  @qr "upiGlobal://pay?ver=01&mode=16&pa=restaurant@mercury&pn=Dubai%20Fine%20Dining&mc=5812&bAm=5.00&bCurr=AED&am=120.66&QRexpire=2026-07-09T15:16:11+05:30&QRts=2026-07-09T14:46:11+05:30&sign=X"
+
+  # What UpiXmlSchema.parse_req_pay/1 returns for the ACC_IP_01 style credit ReqPay (test A)
+  @req_pay %{
+    msg_id: "NPCIREQPAY00000000000000000000001",
+    txn_id: "NPCITXNPAY001-LONGER-THAN-35-CHARACTERS-AS-SENT",
+    note: "ACC_IP-01 final.note",
+    cust_ref: "CUSTREF01",
+    ref_id: "REFNPCI0001",
+    org_txn_id: "ORGTXN0001",
+    ref_category: "00",
+    txn_type: "CREDIT",
+    purpose: "62",
+    sub_type: "PAY",
+    initiation_mode: "16",
+    txn_ts: "2026-07-09T14:46:31+05:30",
+    qr_ver: "01",
+    qr_ts: "2026-07-09T14:46:11+05:30",
+    qr_medium: "03",
+    expire_ts: "2026-07-09T15:16:11+05:30",
+    payee_addr: "restaurant@mercury",
+    payee_name: "NPCI Tester",
+    payee_code: "5812",
+    payee_ifsc: "TEST0000001",
+    payee_ac_num: "123456789012345678",
+    payee_ac_type: "SAVINGS",
+    payee_amount: "5.00",
+    payee_currency: "INR",
+    qr_payload: @qr,
+    net_inst_id: "MER1010001"
+  }
+
+  setup do
+    previous = Application.get_env(:upi_core, :xml_signing_priv_key_path)
+    Application.put_env(:upi_core, :xml_signing_priv_key_path, @key_path)
+
+    on_exit(fn ->
+      if previous,
+        do: Application.put_env(:upi_core, :xml_signing_priv_key_path, previous),
+        else: Application.delete_env(:upi_core, :xml_signing_priv_key_path)
+    end)
+
+    :ok
+  end
+
+  defp success_data(overrides \\ %{}) do
+    Map.merge(
+      %{
+        org_id: "MER101",
+        req_msg_id: "NPCIREQPAY00000000000000000000001",
+        result: "SUCCESS",
+        err_code: "00",
+        resp_code: "00",
+        approval_num: "ABC123",
+        # values a caller may still pass that must NOT win over the ReqPay
+        payee_addr: "db@mercury",
+        reg_name: "Some LLC",
+        ifsc: "MERC0000001",
+        ac_num: "1234567890",
+        acc_type: "CURRENT",
+        org_amount: "120.66",
+        sett_amount: "120.66",
+        sett_currency: "AED",
+        request_data: @req_pay
+      },
+      overrides
+    )
+  end
+
+  defp gen(data) do
+    {:ok, xml} = UpiXmlSchema.generate_resp_pay(data)
+    xml
+  end
+
+  defp attr(xml, path), do: xpath(xml, ~x"#{path}"s)
+  @ref "//*[local-name()='Ref']"
+  @txn "//*[local-name()='Txn']"
+
+  describe "SUCCESS (test A)" do
+    setup do
+      %{xml: gen(success_data())}
+    end
+
+    test "only <Ref> in <Resp>: no <Merchant> and no <Institution>", %{xml: xml} do
+      assert attr(xml, "count(//*[local-name()='Resp']/*)") == "1"
+      assert attr(xml, "count(//*[local-name()='Merchant'])") == "0"
+      assert attr(xml, "count(//*[local-name()='Institution'])") == "0"
+    end
+
+    test "Ref comes from the ReqPay Payee, not from the caller or the DB", %{xml: xml} do
+      assert attr(xml, @ref <> "/@code") == "5812"
+      assert attr(xml, @ref <> "/@addr") == "restaurant@mercury"
+      assert attr(xml, @ref <> "/@regName") == "NPCI Tester"
+      assert attr(xml, @ref <> "/@IFSC") == "TEST0000001"
+      assert attr(xml, @ref <> "/@acNum") == "123456789012345678"
+      assert attr(xml, @ref <> "/@accType") == "SAVINGS"
+      assert attr(xml, @ref <> "/@respCode") == "00"
+    end
+
+    test "orgAmount = settAmount = 5.00 and settCurrency is INR", %{xml: xml} do
+      assert attr(xml, @ref <> "/@orgAmount") == "5.00"
+      assert attr(xml, @ref <> "/@settAmount") == "5.00"
+      assert attr(xml, @ref <> "/@settCurrency") == "INR"
+    end
+
+    test "no errCode on SUCCESS", %{xml: xml} do
+      assert attr(xml, "//*[local-name()='Resp']/@result") == "SUCCESS"
+      assert attr(xml, "//*[local-name()='Resp']/@errCode") == ""
+      assert attr(xml, "//*[local-name()='Resp']/@reqMsgId") == "NPCIREQPAY00000000000000000000001"
+    end
+
+    test "Txn echoes ReqPay as received", %{xml: xml} do
+      assert attr(xml, @txn <> "/@id") == "NPCITXNPAY001-LONGER-THAN-35-CHARACTERS-AS-SENT"
+      assert attr(xml, @txn <> "/@note") == "ACC_IP-01 final.note"
+      assert attr(xml, @txn <> "/@refId") == "REFNPCI0001"
+      assert attr(xml, @txn <> "/@custRef") == "CUSTREF01"
+      assert attr(xml, @txn <> "/@ts") == "2026-07-09T14:46:31+05:30"
+      assert attr(xml, @txn <> "/@type") == "CREDIT"
+      assert attr(xml, @txn <> "/@subType") == "PAY"
+      assert attr(xml, @txn <> "/@initiationMode") == "16"
+      assert attr(xml, @txn <> "/@purpose") == "62"
+      assert attr(xml, @txn <> "/@orgTxnId") == "ORGTXN0001"
+      assert attr(xml, @txn <> "/@refCategory") == "00"
+    end
+
+    test "QR echoes the ReqPay QR element, QR ts is the QR's ts (not Txn ts)", %{xml: xml} do
+      assert attr(xml, "//*[local-name()='QR']/@expireTs") == "2026-07-09T15:16:11+05:30"
+      assert attr(xml, "//*[local-name()='QR']/@qVer") == "01"
+      assert attr(xml, "//*[local-name()='QR']/@qrMedium") == "03"
+      assert attr(xml, "//*[local-name()='QR']/@ts") == "2026-07-09T14:46:11+05:30"
+    end
+
+    test "RiskScores 00999 is kept", %{xml: xml} do
+      assert attr(xml, "//*[local-name()='Score']/@value") == "00999"
+    end
+  end
+
+  describe "amount formatting" do
+    test "150.0 -> 150.00 for both orgAmount and settAmount" do
+      xml = gen(success_data(%{request_data: Map.put(@req_pay, :payee_amount, "150.0")}))
+      assert attr(xml, @ref <> "/@orgAmount") == "150.00"
+      assert attr(xml, @ref <> "/@settAmount") == "150.00"
+    end
+  end
+
+  describe "values that are missing in the request" do
+    test "no generated QR values" do
+      req = Map.drop(@req_pay, [:expire_ts, :qr_ver, :qr_medium, :qr_ts])
+      xml = gen(success_data(%{request_data: req}))
+      assert attr(xml, "//*[local-name()='QR']/@expireTs") == ""
+      assert attr(xml, "//*[local-name()='QR']/@qVer") == ""
+      assert attr(xml, "//*[local-name()='QR']/@qrMedium") == ""
+      assert attr(xml, "//*[local-name()='QR']/@ts") == ""
+    end
+
+    test "no generated Txn id" do
+      xml = gen(success_data(%{request_data: Map.delete(@req_pay, :txn_id)}))
+      assert attr(xml, @txn <> "/@id") == ""
+    end
+  end
+
+  describe "FAILURE" do
+    test "errCode equals Ref respCode, settAmount 0.00, settCurrency INR, orgAmount as the caller sends it" do
+      xml =
+        gen(
+          success_data(%{
+            result: "FAILURE",
+            err_code: "PE",
+            resp_code: "PE",
+            org_amount: "0.00",
+            sett_amount: "5.00",
+            sett_currency: "AED"
+          })
+        )
+
+      assert attr(xml, "//*[local-name()='Resp']/@result") == "FAILURE"
+      assert attr(xml, "//*[local-name()='Resp']/@errCode") == "PE"
+      assert attr(xml, @ref <> "/@respCode") == "PE"
+      assert attr(xml, @ref <> "/@orgAmount") == "0.00"
+      assert attr(xml, @ref <> "/@settAmount") == "0.00"
+      assert attr(xml, @ref <> "/@settCurrency") == "INR"
+      assert attr(xml, @ref <> "/@regName") == "NPCI Tester"
+    end
+
+    test "X7 keeps the 5.00 orgAmount the handler sends (not changed, waiting for NIPL)" do
+      xml = gen(success_data(%{result: "FAILURE", err_code: "X7", resp_code: "X7", org_amount: "5.00"}))
+      assert attr(xml, @ref <> "/@orgAmount") == "5.00"
+      assert attr(xml, @ref <> "/@settAmount") == "0.00"
+    end
+
+    test "errCode falls back to err_code when there is no resp_code, never to \"02\"" do
+      xml = gen(success_data(%{result: "FAILURE", err_code: "YH", resp_code: nil}))
+      assert attr(xml, "//*[local-name()='Resp']/@errCode") == "YH"
+      assert attr(xml, @ref <> "/@respCode") == "YH"
+
+      xml = gen(success_data(%{result: "FAILURE", err_code: nil, resp_code: nil}))
+      assert attr(xml, "//*[local-name()='Resp']/@errCode") == ""
+    end
+  end
+
+  describe "XML escaping" do
+    test "echoed values are escaped" do
+      req = %{@req_pay | payee_name: "A&B <Ltd>", note: ~s(say "hi")}
+      xml = gen(success_data(%{request_data: req}))
+      assert xml =~ ~s(regName="A&amp;B &lt;Ltd&gt;")
+      assert attr(xml, @ref <> "/@regName") == "A&B <Ltd>"
+      assert attr(xml, @txn <> "/@note") == ~s(say "hi")
+    end
+  end
+
+  test "generate_credit_response is gone, callers use generate_resp_pay" do
+    refute function_exported?(UpiXmlSchema, :generate_credit_response, 1)
+  end
+
+  describe "error handlers feed the same generator" do
+    test "X7 (dynamic) - Ref from ReqPay, orgAmount 5.00, settAmount 0.00, errCode X7" do
+      {:ok, data} = UpiDynamic.ErrorHandlers.X7ErrorHandler.generate_x7_error_response(@req_pay, "5.00")
+      xml = gen(data)
+      assert attr(xml, "//*[local-name()='Resp']/@errCode") == "X7"
+      assert attr(xml, @ref <> "/@respCode") == "X7"
+      assert attr(xml, @ref <> "/@orgAmount") == "5.00"
+      assert attr(xml, @ref <> "/@settAmount") == "0.00"
+      assert attr(xml, @ref <> "/@settCurrency") == "INR"
+      assert attr(xml, @ref <> "/@acNum") == "123456789012345678"
+      assert attr(xml, @ref <> "/@code") == "5812"
+      assert attr(xml, @txn <> "/@id") == @req_pay.txn_id
+      assert attr(xml, "//*[local-name()='QR']/@ts") == "2026-07-09T14:46:11+05:30"
+      assert attr(xml, "count(//*[local-name()='Merchant'])") == "0"
+    end
+
+    test "PE - orgAmount 0.00 on FAILURE, Ref from ReqPay" do
+      {:ok, data} = UpiCore.ErrorHandlers.PeErrorHandler.generate_pe_error_response(@req_pay)
+      xml = gen(data)
+      assert attr(xml, "//*[local-name()='Resp']/@errCode") == "PE"
+      assert attr(xml, @ref <> "/@orgAmount") == "0.00"
+      assert attr(xml, @ref <> "/@settAmount") == "0.00"
+      assert attr(xml, @ref <> "/@regName") == "NPCI Tester"
+      assert attr(xml, @ref <> "/@IFSC") == "TEST0000001"
+      assert attr(xml, "//*[local-name()='QR']/@expireTs") == "2026-07-09T15:16:11+05:30"
+    end
+  end
+end
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 f62be5f..5b2c723 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
@@ -2668,7 +2668,10 @@ defmodule UpiDynamic.Api.V1.UpiController do
                         error_response_data =
                           build_error_response_data(parsed_data, "96", "System malfunction")
 
-                        case UpiXmlSchema.generate_credit_response(error_response_data) do
+                        # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+                        # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+                        # OLD:                         case UpiXmlSchema.generate_credit_response(error_response_data) do
+                        case UpiXmlSchema.generate_resp_pay(error_response_data) do
                           {:ok, error_xml} ->
                             txn_id = parsed_data.txn_id || org_txn_id
 
@@ -2822,26 +2825,33 @@ defmodule UpiDynamic.Api.V1.UpiController do
       req_msg_id: msg_id,
       result: processed_data.result,
       err_code: processed_data.err_code,
-
-      # Payee reference
-      payee_ref: %{
-        addr: parsed_data.payee_addr,
-        code: parsed_data.payee_code || "8999",
-        seq_num: "1",
-        type: "PAYEE",
-        ifsc: parsed_data.payee_ifsc || "MSWI0000000",
-        ac_num: parsed_data.payee_ac_num,
-        acc_type: parsed_data.payee_ac_type || "CURRENT",
-        reg_name: parsed_data.payee_name || "Unknown Merchant",
-        org_amount: parsed_data.amount,
-        resp_code: processed_data.resp_code,
-        sett_amount: processed_data.sett_amount || "0.00",
-        sett_currency: parsed_data.payee_currency || "INR"
-      }
+      # [RespPay Ref.respCode / request echo] ADDED: generate_resp_pay builds Txn / QR / Ref from the ReqPay it is given
+      resp_code: processed_data.resp_code,
+      request_data: parsed_data,
+
+      # [RespPay Ref payee_ref] REMOVED: unused by generate_resp_pay (Ref comes from request_data); it carried invented defaults 8999 / MSWI0000000 / CURRENT / Unknown Merchant
+      # OLD:       # Payee reference
+      # OLD:       payee_ref: %{
+      # OLD:         addr: parsed_data.payee_addr,
+      # OLD:         code: parsed_data.payee_code || "8999",
+      # OLD:         seq_num: "1",
+      # OLD:         type: "PAYEE",
+      # OLD:         ifsc: parsed_data.payee_ifsc || "MSWI0000000",
+      # OLD:         ac_num: parsed_data.payee_ac_num,
+      # OLD:         acc_type: parsed_data.payee_ac_type || "CURRENT",
+      # OLD:         reg_name: parsed_data.payee_name || "Unknown Merchant",
+      # OLD:         org_amount: parsed_data.amount,
+      # OLD:         resp_code: processed_data.resp_code,
+      # OLD:         sett_amount: processed_data.sett_amount || "0.00",
+      # OLD:         sett_currency: parsed_data.payee_currency || "INR"
+      # OLD:       }
     }
 
     # Generate SUCCESS RespPay XML
-    case UpiXmlSchema.generate_credit_response(response_data) do
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     case UpiXmlSchema.generate_credit_response(response_data) do
+    case UpiXmlSchema.generate_resp_pay(response_data) do
       {:ok, response_xml} ->
         Logger.info(
           "[ASYNC Processing] Generated successful RespPay XML for orgTxnId=#{org_txn_id}"
@@ -2878,7 +2888,9 @@ defmodule UpiDynamic.Api.V1.UpiController do
     # Build failure response data
     error_response_data = %{
       msg_id: "MSW" <> generate_message_id(),
-      org_id: "MSW102",
+      # [RespPay Head.orgId] CHANGED: PSP orgId (MER101), "MSW102" is not ours
+      # OLD:       org_id: "MSW102",
+      org_id: "MER101",
       ts: parsed_data.timestamp || DateTime.utc_now() |> DateTime.to_iso8601(),
       ver: "2.0",
 
@@ -2905,28 +2917,35 @@ defmodule UpiDynamic.Api.V1.UpiController do
       req_msg_id: msg_id,
       result: "FAILURE",
       err_code: err_code,
-
-      # Payee reference with failure codes
-      payee_ref: %{
-        addr: parsed_data.payee_addr,
-        code: parsed_data.payee_code || "8999",
-        seq_num: "1",
-        type: "PAYEE",
-        ifsc: parsed_data.payee_ifsc || "MSWI0000000",
-        ac_num: parsed_data.payee_ac_num,
-        acc_type: parsed_data.payee_ac_type || "CURRENT",
-        reg_name: parsed_data.payee_name || "Unknown Merchant",
-        org_amount: parsed_data.amount,
-        # Use error code as response code
-        resp_code: err_code,
-        # No settlement amount for failed transactions
-        sett_amount: "0.00",
-        sett_currency: parsed_data.payee_currency || "INR"
-      }
+      # [RespPay Ref.respCode / request echo] ADDED: Ref respCode = errCode, Txn / QR / Ref come from the ReqPay
+      resp_code: err_code,
+      request_data: parsed_data,
+
+      # [RespPay Ref payee_ref] REMOVED: unused by generate_resp_pay (Ref comes from request_data); it carried invented defaults 8999 / MSWI0000000 / CURRENT / Unknown Merchant
+      # OLD:       # Payee reference with failure codes
+      # OLD:       payee_ref: %{
+      # OLD:         addr: parsed_data.payee_addr,
+      # OLD:         code: parsed_data.payee_code || "8999",
+      # OLD:         seq_num: "1",
+      # OLD:         type: "PAYEE",
+      # OLD:         ifsc: parsed_data.payee_ifsc || "MSWI0000000",
+      # OLD:         ac_num: parsed_data.payee_ac_num,
+      # OLD:         acc_type: parsed_data.payee_ac_type || "CURRENT",
+      # OLD:         reg_name: parsed_data.payee_name || "Unknown Merchant",
+      # OLD:         org_amount: parsed_data.amount,
+      # OLD:         # Use error code as response code
+      # OLD:         resp_code: err_code,
+      # OLD:         # No settlement amount for failed transactions
+      # OLD:         sett_amount: "0.00",
+      # OLD:         sett_currency: parsed_data.payee_currency || "INR"
+      # OLD:       }
     }
 
     # Generate FAILURE RespPay XML
-    case UpiXmlSchema.generate_credit_response(error_response_data) do
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     case UpiXmlSchema.generate_credit_response(error_response_data) do
+    case UpiXmlSchema.generate_resp_pay(error_response_data) do
       {:ok, response_xml} ->
         Logger.info(
           "[ASYNC Processing] Generated FAILURE RespPay XML for orgTxnId=#{org_txn_id} - Error: #{error_message}"
@@ -2974,6 +2993,8 @@ defmodule UpiDynamic.Api.V1.UpiController do
       req_msg_id: parsed_data.msg_id || "unknown",
       result: "FAILURE",
       err_code: err_code,
+      resp_code: err_code,
+      request_data: parsed_data,
       type: "CREDIT",
       payee_ref: %{
         addr: parsed_data.payee_addr,
@@ -2985,7 +3006,10 @@ defmodule UpiDynamic.Api.V1.UpiController do
   end
 
   defp generate_error_response_xml(error_response_data, org_txn_id) do
-    case UpiXmlSchema.generate_credit_response(error_response_data) do
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     case UpiXmlSchema.generate_credit_response(error_response_data) do
+    case UpiXmlSchema.generate_resp_pay(error_response_data) do
       {:ok, error_xml} ->
         Logger.info("[ASYNC Processing] Generated error RespPay XML for orgTxnId=#{org_txn_id}")
         {:error, {error_xml, error_response_data.err_code}}
@@ -3334,7 +3358,9 @@ defmodule UpiDynamic.Api.V1.UpiController do
   defp send_error_response(conn, err_code, error_message) do
     response_data = %{
       msg_id: "ERR" <> generate_message_id(),
-      org_id: "MSW102",
+      # [RespPay Head.orgId] CHANGED: PSP orgId (MER101), "MSW102" is not ours
+      # OLD:       org_id: "MSW102",
+      org_id: "MER101",
       ts: DateTime.utc_now() |> DateTime.to_iso8601(),
       ver: "2.0",
       txn_id: "N/A",
@@ -3353,23 +3379,28 @@ defmodule UpiDynamic.Api.V1.UpiController do
       req_msg_id: "N/A",
       result: "FAILURE",
       err_code: err_code,
-      payee_ref: %{
-        addr: "N/A",
-        code: "8999",
-        seq_num: "1",
-        type: "PAYEE",
-        ifsc: "MSWI0000000",
-        ac_num: "N/A",
-        acc_type: "CURRENT",
-        reg_name: "N/A",
-        org_amount: "0.00",
-        resp_code: err_code,
-        sett_amount: "0.00",
-        sett_currency: "INR"
-      }
+      resp_code: err_code,
+      # [RespPay Ref payee_ref] REMOVED: unused by generate_resp_pay; it carried invented values N/A / 8999 / MSWI0000000. NOTE: this generic error has no ReqPay to echo (request unreadable), so its Txn / Ref stay empty or N/A
+      # OLD:       payee_ref: %{
+      # OLD:         addr: "N/A",
+      # OLD:         code: "8999",
+      # OLD:         seq_num: "1",
+      # OLD:         type: "PAYEE",
+      # OLD:         ifsc: "MSWI0000000",
+      # OLD:         ac_num: "N/A",
+      # OLD:         acc_type: "CURRENT",
+      # OLD:         reg_name: "N/A",
+      # OLD:         org_amount: "0.00",
+      # OLD:         resp_code: err_code,
+      # OLD:         sett_amount: "0.00",
+      # OLD:         sett_currency: "INR"
+      # OLD:       }
     }
 
-    {:ok, xml} = UpiXmlSchema.generate_credit_response(response_data)
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     {:ok, xml} = UpiXmlSchema.generate_credit_response(response_data)
+    {:ok, xml} = UpiXmlSchema.generate_resp_pay(response_data)
 
     conn
     |> put_resp_content_type("application/xml")
diff --git a/apps/upi_dynamic/lib/upi_dynamic/error_handlers/yh_error_handler.ex b/apps/upi_dynamic/lib/upi_dynamic/error_handlers/yh_error_handler.ex
index de8b75b..3d09f18 100644
--- a/apps/upi_dynamic/lib/upi_dynamic/error_handlers/yh_error_handler.ex
+++ b/apps/upi_dynamic/lib/upi_dynamic/error_handlers/yh_error_handler.ex
@@ -268,8 +268,11 @@ defmodule UpiDynamic.ErrorHandlers.YhErrorHandler do
       ref_id: Map.get(parsed_data, :ref_id, ""),
       ref_url: "https://mercurypay.aritic.app",
       expire_ts: Map.get(parsed_data, :expire_ts),
-      qr_ver: Map.get(parsed_data, :qr_ver, "02"),
-      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (02 / 03); generate_resp_pay echoes the ReqPay QR element
+      # OLD:       qr_ver: Map.get(parsed_data, :qr_ver, "02"),
+      # OLD:       qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      qr_ver: Map.get(parsed_data, :qr_ver),
+      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, Map.get(parsed_data, :qr_medium)),
       sp_risk_score: "0",
       npci_risk_score: Map.get(parsed_data, :npci_risk_score, "0"),
 
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 184d08d..91b4e59 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
@@ -283,8 +283,11 @@ defmodule UpiDynamic.SpecialHandlers.AccIp05Scenario do
       ref_id: Map.get(parsed_data, :ref_id, ""),
       ref_url: "https://mercurypay.aritic.app",
       expire_ts: Map.get(parsed_data, :expire_ts),
-      qr_ver: Map.get(parsed_data, :qr_ver, "02"),
-      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (02 / 03); generate_resp_pay echoes the ReqPay QR element
+      # OLD:       qr_ver: Map.get(parsed_data, :qr_ver, "02"),
+      # OLD:       qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      qr_ver: Map.get(parsed_data, :qr_ver),
+      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, Map.get(parsed_data, :qr_medium)),
       sp_risk_score: "0",
       npci_risk_score: Map.get(parsed_data, :npci_risk_score, "0"),
 
diff --git a/apps/upi_dynamic/lib/upi_dynamic/upi_transaction_manager.ex b/apps/upi_dynamic/lib/upi_dynamic/upi_transaction_manager.ex
index 2272ee7..cdf47bb 100644
--- a/apps/upi_dynamic/lib/upi_dynamic/upi_transaction_manager.ex
+++ b/apps/upi_dynamic/lib/upi_dynamic/upi_transaction_manager.ex
@@ -910,7 +910,9 @@ end
                   cust_ref: parsed_data.cust_ref,
                   txn_type: parsed_data.txn_type || "CREDIT",  # CRITICAL: Use original txn_type from ReqPay for T07 compliance
                   add_info: build_additional_info(partner_response),
-                  request_data: parsed_data
+                  # [RespPay request_data] CHANGED: pass the raw parsed ReqPay (parsed_data was normalize_parsed_data output, which drops the Payee / QR keys the Ref and QR need)
+                  # OLD:                   request_data: parsed_data
+                  request_data: parsed_data_raw
                 }
 
                 UpiXmlSchema.generate_resp_pay(response_data)
@@ -1206,20 +1208,28 @@ end
       prod_type: "UPI",
 
       # QR details from request or defaults
-      qr_ver: "2.0",
-      qr_medium: "04",
-      expire_ts: calculate_qr_expire_ts(parsed_data),
+      # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (2.0 / 04 / now+5min expire); generate_resp_pay echoes the ReqPay QR element
+      # [RespPay QR.expireTs] REMOVED: calculate_qr_expire_ts fallback; TSD Table 4: expireTs = ReqPay QR expireTs
+      # OLD:       qr_ver: "2.0",
+      # OLD:       qr_medium: "04",
+      # OLD:       expire_ts: calculate_qr_expire_ts(parsed_data),
       ver_token: generate_ver_token(),
       stan: generate_stan(),
 
       # Merchant/Payee details
       payee_addr: merchant_info.merchant_vpa,
-      payee_code: Map.get(parsed_data, :payee_code) || merchant_info.merchant_code || "0000",
+      # [RespPay Ref.code] CHANGED: no invented MCC 0000 (Ref code is the ReqPay Payee code)
+      # OLD:       payee_code: Map.get(parsed_data, :payee_code) || merchant_info.merchant_code || "0000",
+      payee_code: Map.get(parsed_data, :payee_code) || merchant_info.merchant_code,
       payee_name: merchant_info.brand_name,
       reg_name: merchant_info.legal_name || merchant_info.brand_name,
-      ifsc: merchant_info.ifsc_code || "MERC0000001",
-      ac_num: merchant_info.account_number || "1234567890",
-      acc_type: merchant_info.account_type || "CURRENT",
+      # [RespPay Ref.IFSC / Ref.acNum / Ref.accType] CHANGED: no dummy MERC0000001 / 1234567890 / CURRENT (Ref comes from the ReqPay Payee Ac)
+      # OLD:       ifsc: merchant_info.ifsc_code || "MERC0000001",
+      # OLD:       ac_num: merchant_info.account_number || "1234567890",
+      # OLD:       acc_type: merchant_info.account_type || "CURRENT",
+      ifsc: merchant_info.ifsc_code,
+      ac_num: merchant_info.account_number,
+      acc_type: merchant_info.account_type,
 
       # Amount details - use cu= and am= for settlement (INR amount/currency)
       org_amount: extract_amount_from_qr(Map.get(parsed_data, :qr_payload, "")) ||
@@ -1252,64 +1262,66 @@ end
   end
 
   # Calculate QR expire timestamp for RespPay
-  defp calculate_qr_expire_ts(parsed_data) do
-    # Use expire_ts from request source, fallback to calculated value
-    request_expire_ts = Map.get(parsed_data, :expire_ts)
-
-    cond do
-      request_expire_ts && request_expire_ts != "" ->
-        Logger.info("QR ExpireTs calculation - request_source.expire_ts: \"#{request_expire_ts}\"")
-        request_expire_ts
-
-      true ->
-        # Calculate from QR timestamp or current time
-        qr_ts = extract_qr_ts_from_payload(Map.get(parsed_data, :qr_payload, ""))
-        if qr_ts do
-          # Add 18 minutes to QR timestamp as per UPI spec
-          expire_time = DateTime.add(qr_ts, 18 * 60, :second)
-          expire_ts = DateTime.to_iso8601(expire_time)
-          Logger.info("QR ExpireTs calculation - calculated from QR ts: \"#{expire_ts}\"")
-          expire_ts
-        else
-          # Default: current time + 18 minutes
-          expire_time = DateTime.add(DateTime.utc_now(), 18 * 60, :second)
-          expire_ts = DateTime.to_iso8601(expire_time)
-          Logger.info("QR ExpireTs calculation - default calculated: \"#{expire_ts}\"")
-          expire_ts
-        end
-    end
-  end
+  # [RespPay QR / Txn fallbacks] REMOVED: calculate_qr_expire_ts (fell back to now+5min) is unused now - QR and Txn values are echoed from ReqPay, nothing is generated
+  # OLD:   defp calculate_qr_expire_ts(parsed_data) do
+  # OLD:     # Use expire_ts from request source, fallback to calculated value
+  # OLD:     request_expire_ts = Map.get(parsed_data, :expire_ts)
+  # OLD:
+  # OLD:     cond do
+  # OLD:       request_expire_ts && request_expire_ts != "" ->
+  # OLD:         Logger.info("QR ExpireTs calculation - request_source.expire_ts: \"#{request_expire_ts}\"")
+  # OLD:         request_expire_ts
+  # OLD:
+  # OLD:       true ->
+  # OLD:         # Calculate from QR timestamp or current time
+  # OLD:         qr_ts = extract_qr_ts_from_payload(Map.get(parsed_data, :qr_payload, ""))
+  # OLD:         if qr_ts do
+  # OLD:           # Add 18 minutes to QR timestamp as per UPI spec
+  # OLD:           expire_time = DateTime.add(qr_ts, 18 * 60, :second)
+  # OLD:           expire_ts = DateTime.to_iso8601(expire_time)
+  # OLD:           Logger.info("QR ExpireTs calculation - calculated from QR ts: \"#{expire_ts}\"")
+  # OLD:           expire_ts
+  # OLD:         else
+  # OLD:           # Default: current time + 18 minutes
+  # OLD:           expire_time = DateTime.add(DateTime.utc_now(), 18 * 60, :second)
+  # OLD:           expire_ts = DateTime.to_iso8601(expire_time)
+  # OLD:           Logger.info("QR ExpireTs calculation - default calculated: \"#{expire_ts}\"")
+  # OLD:           expire_ts
+  # OLD:         end
+  # OLD:     end
+  # OLD:   end
 
   # Extract QR timestamp from QR payload for expiry calculations
-  defp extract_qr_ts_from_payload(qr_payload) when is_binary(qr_payload) and qr_payload != "" do
-    # Try to extract timestamp from QR payload using various patterns
-    cond do
-      # Look for QRts parameter in QR payload
-      String.contains?(qr_payload, "QRts=") ->
-        case Regex.run(~r/QRts=([^&]+)/, qr_payload) do
-          [_, timestamp_str] ->
-            case DateTime.from_iso8601(timestamp_str) do
-              {:ok, datetime, _} -> datetime
-              _ -> nil
-            end
-          _ -> nil
-        end
-
-      # Look for ts parameter
-      String.contains?(qr_payload, "ts=") ->
-        case Regex.run(~r/ts=([^&]+)/, qr_payload) do
-          [_, timestamp_str] ->
-            case DateTime.from_iso8601(timestamp_str) do
-              {:ok, datetime, _} -> datetime
-              _ -> nil
-            end
-          _ -> nil
-        end
-
-      true -> nil
-    end
-  end
-  defp extract_qr_ts_from_payload(_), do: nil
+  # [RespPay QR / Txn fallbacks] REMOVED: extract_qr_ts_from_payload (private copy) is unused now - QR and Txn values are echoed from ReqPay, nothing is generated
+  # OLD:   defp extract_qr_ts_from_payload(qr_payload) when is_binary(qr_payload) and qr_payload != "" do
+  # OLD:     # Try to extract timestamp from QR payload using various patterns
+  # OLD:     cond do
+  # OLD:       # Look for QRts parameter in QR payload
+  # OLD:       String.contains?(qr_payload, "QRts=") ->
+  # OLD:         case Regex.run(~r/QRts=([^&]+)/, qr_payload) do
+  # OLD:           [_, timestamp_str] ->
+  # OLD:             case DateTime.from_iso8601(timestamp_str) do
+  # OLD:               {:ok, datetime, _} -> datetime
+  # OLD:               _ -> nil
+  # OLD:             end
+  # OLD:           _ -> nil
+  # OLD:         end
+  # OLD:
+  # OLD:       # Look for ts parameter
+  # OLD:       String.contains?(qr_payload, "ts=") ->
+  # OLD:         case Regex.run(~r/ts=([^&]+)/, qr_payload) do
+  # OLD:           [_, timestamp_str] ->
+  # OLD:             case DateTime.from_iso8601(timestamp_str) do
+  # OLD:               {:ok, datetime, _} -> datetime
+  # OLD:               _ -> nil
+  # OLD:             end
+  # OLD:           _ -> nil
+  # OLD:         end
+  # OLD:
+  # OLD:       true -> nil
+  # OLD:     end
+  # OLD:   end
+  # OLD:   defp extract_qr_ts_from_payload(_), do: nil
 
   # Process QR payment transaction and send RespPay to NPCI
   defp process_qr_payment_and_send_response(parsed_data, merchant_info) do
@@ -1378,12 +1390,18 @@ end
 
               # Merchant/Payee details
               payee_addr: merchant_info.merchant_vpa,
-              payee_code: Map.get(parsed_data, :payee_code) || merchant_info.merchant_code || "0000",
+              # [RespPay Ref.code] CHANGED: no invented MCC 0000 (Ref code is the ReqPay Payee code)
+              # OLD:               payee_code: Map.get(parsed_data, :payee_code) || merchant_info.merchant_code || "0000",
+              payee_code: Map.get(parsed_data, :payee_code) || merchant_info.merchant_code,
               payee_name: merchant_info.brand_name,
               reg_name: merchant_info.legal_name || merchant_info.brand_name,
-              ifsc: merchant_info.ifsc_code || "MERC0000001",
-              ac_num: merchant_info.account_number || "1234567890",
-              acc_type: merchant_info.account_type || "CURRENT",
+              # [RespPay Ref.IFSC / Ref.acNum / Ref.accType] CHANGED: no dummy MERC0000001 / 1234567890 / CURRENT (Ref comes from the ReqPay Payee Ac)
+              # OLD:               ifsc: merchant_info.ifsc_code || "MERC0000001",
+              # OLD:               ac_num: merchant_info.account_number || "1234567890",
+              # OLD:               acc_type: merchant_info.account_type || "CURRENT",
+              ifsc: merchant_info.ifsc_code,
+              ac_num: merchant_info.account_number,
+              acc_type: merchant_info.account_type,
 
               # Amount details - use cu= and am= for settlement (INR amount/currency)
               org_amount: extract_amount_from_qr(Map.get(parsed_data, :qr_payload, "")) ||
@@ -1395,8 +1413,9 @@ end
                             Map.get(parsed_data, :payee_currency, "INR"),
 
               # QR details
-              qr_ver: "2.0",
-              qr_medium: "04",
+              # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (2.0 / 04); generate_resp_pay echoes the ReqPay QR element
+              # OLD:               qr_ver: "2.0",
+              # OLD:               qr_medium: "04",
               ver_token: generate_ver_token(),
               stan: generate_stan(),
 
@@ -4465,8 +4484,11 @@ end
                       sp_risk_score: validated_data.sp_risk_score || "0",
                       npci_risk_score: validated_data.npci_risk_score || "0",
                       expire_ts: validated_data.expire_ts,
-                      qr_ver: validated_data.qr_ver || "2.0",
-                      qr_medium: validated_data.qr_medium || "04",
+                      # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (2.0 / 04); generate_resp_pay echoes the ReqPay QR element
+                      # OLD:                       qr_ver: validated_data.qr_ver || "2.0",
+                      # OLD:                       qr_medium: validated_data.qr_medium || "04",
+                      qr_ver: validated_data.qr_ver,
+                      qr_medium: validated_data.qr_medium,
                       qr_query: validated_data.qr_query || "",
                       ver_token: validated_data.ver_token,
                       stan: validated_data.stan,
@@ -4760,10 +4782,14 @@ end
     response_data = %{
       org_id: get_psp_org_id(),
       msg_id: generate_message_id(),
-      req_msg_id: Map.get(parsed_data, :msg_id, "UNKNOWN"),
+      # [RespPay Resp.reqMsgId] CHANGED: no "UNKNOWN" placeholder
+      # OLD:       req_msg_id: Map.get(parsed_data, :msg_id, "UNKNOWN"),
+      req_msg_id: Map.get(parsed_data, :msg_id),
       result: "FAILURE",
       err_code: err_code,
-      txn_id: Map.get(parsed_data, :txn_id, "UNKNOWN"),
+      # [RespPay Txn.id] CHANGED: no "UNKNOWN" placeholder (echo of the ReqPay Txn id)
+      # OLD:       txn_id: Map.get(parsed_data, :txn_id, "UNKNOWN"),
+      txn_id: Map.get(parsed_data, :txn_id),
       note: error_message,
       ref_id: Map.get(parsed_data, :ref_id, ""),
       cust_ref: Map.get(parsed_data, :cust_ref, ""),
@@ -4771,25 +4797,38 @@ end
       txn_type: final_txn_type,
       sub_type: Map.get(parsed_data, :sub_type, ""),
       initiation_mode: Map.get(parsed_data, :initiation_mode, "QR"),
-      org_txn_id: Map.get(parsed_data, :org_txn_id, "UNKNOWN"),
+      # [RespPay Txn.orgTxnId] CHANGED: no "UNKNOWN" placeholder
+      # OLD:       org_txn_id: Map.get(parsed_data, :org_txn_id, "UNKNOWN"),
+      org_txn_id: Map.get(parsed_data, :org_txn_id),
       org_rrn: Map.get(parsed_data, :org_rrn, ""),
       org_txn_date: Map.get(parsed_data, :org_txn_date, get_date()),
       ref_category: Map.get(parsed_data, :ref_category, ""),
   prod_type: Map.get(parsed_data, :prod_type, "UPI"),
       sp_risk_score: Map.get(parsed_data, :sp_risk_score, "0"),
       npci_risk_score: Map.get(parsed_data, :npci_risk_score, "0"),
-      payee_addr: Map.get(parsed_data, :payee_addr, "UNKNOWN"),
-      payee_code: Map.get(parsed_data, :payee_code, "0000"),
+      # [RespPay Ref.addr / Ref.code] CHANGED: no "UNKNOWN" / 0000 placeholders
+      # OLD:       payee_addr: Map.get(parsed_data, :payee_addr, "UNKNOWN"),
+      # OLD:       payee_code: Map.get(parsed_data, :payee_code, "0000"),
+      payee_addr: Map.get(parsed_data, :payee_addr),
+      payee_code: Map.get(parsed_data, :payee_code),
       org_amount: Map.get(parsed_data, :base_amount, Map.get(parsed_data, :payee_amount, Map.get(parsed_data, :amount, "0.00"))),
       resp_code: err_code,
-      reg_name: Map.get(parsed_data, :merchant_legal_name, Map.get(parsed_data, :payee_name, "UNKNOWN")),
-      ifsc: Map.get(parsed_data, :payee_ifsc, "UNKNOWN"),
-      ac_num: Map.get(parsed_data, :payee_ac_num, "UNKNOWN"),
-      acc_type: Map.get(parsed_data, :payee_ac_type, "CURRENT"),
+      # [RespPay Ref.regName / IFSC / acNum / accType] CHANGED: no "UNKNOWN" / CURRENT placeholders
+      # OLD:       reg_name: Map.get(parsed_data, :merchant_legal_name, Map.get(parsed_data, :payee_name, "UNKNOWN")),
+      # OLD:       ifsc: Map.get(parsed_data, :payee_ifsc, "UNKNOWN"),
+      # OLD:       ac_num: Map.get(parsed_data, :payee_ac_num, "UNKNOWN"),
+      # OLD:       acc_type: Map.get(parsed_data, :payee_ac_type, "CURRENT"),
+      reg_name: Map.get(parsed_data, :merchant_legal_name, Map.get(parsed_data, :payee_name)),
+      ifsc: Map.get(parsed_data, :payee_ifsc),
+      ac_num: Map.get(parsed_data, :payee_ac_num),
+      acc_type: Map.get(parsed_data, :payee_ac_type),
       approval_num: "ERROR_#{System.unique_integer([:positive])}",
       sett_amount: "0.00",
       sett_currency: "INR",
-      expire_ts: DateTime.utc_now() |> DateTime.add(300, :second) |> DateTime.to_iso8601()
+      # [RespPay QR.expireTs] CHANGED: the generated now+5min expire_ts is gone; request_data now carries the ReqPay QR expireTs / qVer / qrMedium / ts
+      # [RespPay request_data] ADDED: international error responder passes the ReqPay (TSD Table 4: Txn / QR / Ref echo ReqPay)
+      # OLD:       expire_ts: DateTime.utc_now() |> DateTime.add(300, :second) |> DateTime.to_iso8601()
+      request_data: parsed_data
     }
 
     UpiXmlSchema.generate_resp_pay(response_data)
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 ff6790c..22b5b76 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
@@ -2621,7 +2621,10 @@ defmodule UpiStatic.Api.V1.UpiController do
                         error_response_data =
                           build_error_response_data(parsed_data, "96", "System malfunction")
 
-                        case UpiXmlSchema.generate_credit_response(error_response_data) do
+                        # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+                        # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+                        # OLD:                         case UpiXmlSchema.generate_credit_response(error_response_data) do
+                        case UpiXmlSchema.generate_resp_pay(error_response_data) do
                           {:ok, error_xml} ->
                             txn_id = parsed_data.txn_id || org_txn_id
 
@@ -2775,26 +2778,33 @@ defmodule UpiStatic.Api.V1.UpiController do
       req_msg_id: msg_id,
       result: processed_data.result,
       err_code: processed_data.err_code,
-
-      # Payee reference
-      payee_ref: %{
-        addr: parsed_data.payee_addr,
-        code: parsed_data.payee_code || "8999",
-        seq_num: "1",
-        type: "PAYEE",
-        ifsc: parsed_data.payee_ifsc || "MSWI0000000",
-        ac_num: parsed_data.payee_ac_num,
-        acc_type: parsed_data.payee_ac_type || "CURRENT",
-        reg_name: parsed_data.payee_name || "Unknown Merchant",
-        org_amount: parsed_data.amount,
-        resp_code: processed_data.resp_code,
-        sett_amount: processed_data.sett_amount || "0.00",
-        sett_currency: parsed_data.payee_currency || "INR"
-      }
+      # [RespPay Ref.respCode / request echo] ADDED: generate_resp_pay builds Txn / QR / Ref from the ReqPay it is given
+      resp_code: processed_data.resp_code,
+      request_data: parsed_data,
+
+      # [RespPay Ref payee_ref] REMOVED: unused by generate_resp_pay (Ref comes from request_data); it carried invented defaults 8999 / MSWI0000000 / CURRENT / Unknown Merchant
+      # OLD:       # Payee reference
+      # OLD:       payee_ref: %{
+      # OLD:         addr: parsed_data.payee_addr,
+      # OLD:         code: parsed_data.payee_code || "8999",
+      # OLD:         seq_num: "1",
+      # OLD:         type: "PAYEE",
+      # OLD:         ifsc: parsed_data.payee_ifsc || "MSWI0000000",
+      # OLD:         ac_num: parsed_data.payee_ac_num,
+      # OLD:         acc_type: parsed_data.payee_ac_type || "CURRENT",
+      # OLD:         reg_name: parsed_data.payee_name || "Unknown Merchant",
+      # OLD:         org_amount: parsed_data.amount,
+      # OLD:         resp_code: processed_data.resp_code,
+      # OLD:         sett_amount: processed_data.sett_amount || "0.00",
+      # OLD:         sett_currency: parsed_data.payee_currency || "INR"
+      # OLD:       }
     }
 
     # Generate SUCCESS RespPay XML
-    case UpiXmlSchema.generate_credit_response(response_data) do
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     case UpiXmlSchema.generate_credit_response(response_data) do
+    case UpiXmlSchema.generate_resp_pay(response_data) do
       {:ok, response_xml} ->
         Logger.info(
           "[ASYNC Processing] Generated successful RespPay XML for orgTxnId=#{org_txn_id}"
@@ -2831,7 +2841,9 @@ defmodule UpiStatic.Api.V1.UpiController do
     # Build failure response data
     error_response_data = %{
       msg_id: "MSW" <> generate_message_id(),
-      org_id: "MSW102",
+      # [RespPay Head.orgId] CHANGED: PSP orgId (MER102), "MSW102" is not ours
+      # OLD:       org_id: "MSW102",
+      org_id: "MER102",
       ts: parsed_data.timestamp || DateTime.utc_now() |> DateTime.to_iso8601(),
       ver: "2.0",
 
@@ -2858,28 +2870,35 @@ defmodule UpiStatic.Api.V1.UpiController do
       req_msg_id: msg_id,
       result: "FAILURE",
       err_code: err_code,
-
-      # Payee reference with failure codes
-      payee_ref: %{
-        addr: parsed_data.payee_addr,
-        code: parsed_data.payee_code || "8999",
-        seq_num: "1",
-        type: "PAYEE",
-        ifsc: parsed_data.payee_ifsc || "MSWI0000000",
-        ac_num: parsed_data.payee_ac_num,
-        acc_type: parsed_data.payee_ac_type || "CURRENT",
-        reg_name: parsed_data.payee_name || "Unknown Merchant",
-        org_amount: parsed_data.amount,
-        # Use error code as response code
-        resp_code: err_code,
-        # No settlement amount for failed transactions
-        sett_amount: "0.00",
-        sett_currency: parsed_data.payee_currency || "INR"
-      }
+      # [RespPay Ref.respCode / request echo] ADDED: Ref respCode = errCode, Txn / QR / Ref come from the ReqPay
+      resp_code: err_code,
+      request_data: parsed_data,
+
+      # [RespPay Ref payee_ref] REMOVED: unused by generate_resp_pay (Ref comes from request_data); it carried invented defaults 8999 / MSWI0000000 / CURRENT / Unknown Merchant
+      # OLD:       # Payee reference with failure codes
+      # OLD:       payee_ref: %{
+      # OLD:         addr: parsed_data.payee_addr,
+      # OLD:         code: parsed_data.payee_code || "8999",
+      # OLD:         seq_num: "1",
+      # OLD:         type: "PAYEE",
+      # OLD:         ifsc: parsed_data.payee_ifsc || "MSWI0000000",
+      # OLD:         ac_num: parsed_data.payee_ac_num,
+      # OLD:         acc_type: parsed_data.payee_ac_type || "CURRENT",
+      # OLD:         reg_name: parsed_data.payee_name || "Unknown Merchant",
+      # OLD:         org_amount: parsed_data.amount,
+      # OLD:         # Use error code as response code
+      # OLD:         resp_code: err_code,
+      # OLD:         # No settlement amount for failed transactions
+      # OLD:         sett_amount: "0.00",
+      # OLD:         sett_currency: parsed_data.payee_currency || "INR"
+      # OLD:       }
     }
 
     # Generate FAILURE RespPay XML
-    case UpiXmlSchema.generate_credit_response(error_response_data) do
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     case UpiXmlSchema.generate_credit_response(error_response_data) do
+    case UpiXmlSchema.generate_resp_pay(error_response_data) do
       {:ok, response_xml} ->
         Logger.info(
           "[ASYNC Processing] Generated FAILURE RespPay XML for orgTxnId=#{org_txn_id} - Error: #{error_message}"
@@ -2927,6 +2946,8 @@ defmodule UpiStatic.Api.V1.UpiController do
       req_msg_id: parsed_data.msg_id || "unknown",
       result: "FAILURE",
       err_code: err_code,
+      resp_code: err_code,
+      request_data: parsed_data,
       type: "CREDIT",
       payee_ref: %{
         addr: parsed_data.payee_addr,
@@ -2938,7 +2959,10 @@ defmodule UpiStatic.Api.V1.UpiController do
   end
 
   defp generate_error_response_xml(error_response_data, org_txn_id) do
-    case UpiXmlSchema.generate_credit_response(error_response_data) do
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     case UpiXmlSchema.generate_credit_response(error_response_data) do
+    case UpiXmlSchema.generate_resp_pay(error_response_data) do
       {:ok, error_xml} ->
         Logger.info("[ASYNC Processing] Generated error RespPay XML for orgTxnId=#{org_txn_id}")
         {:error, {error_xml, error_response_data.err_code}}
@@ -3276,7 +3300,9 @@ defmodule UpiStatic.Api.V1.UpiController do
   defp send_error_response(conn, err_code, error_message) do
     response_data = %{
       msg_id: "ERR" <> generate_message_id(),
-      org_id: "MSW102",
+      # [RespPay Head.orgId] CHANGED: PSP orgId (MER102), "MSW102" is not ours
+      # OLD:       org_id: "MSW102",
+      org_id: "MER102",
       ts: DateTime.utc_now() |> DateTime.to_iso8601(),
       ver: "2.0",
       txn_id: "N/A",
@@ -3295,23 +3321,28 @@ defmodule UpiStatic.Api.V1.UpiController do
       req_msg_id: "N/A",
       result: "FAILURE",
       err_code: err_code,
-      payee_ref: %{
-        addr: "N/A",
-        code: "8999",
-        seq_num: "1",
-        type: "PAYEE",
-        ifsc: "MSWI0000000",
-        ac_num: "N/A",
-        acc_type: "CURRENT",
-        reg_name: "N/A",
-        org_amount: "0.00",
-        resp_code: err_code,
-        sett_amount: "0.00",
-        sett_currency: "INR"
-      }
+      resp_code: err_code,
+      # [RespPay Ref payee_ref] REMOVED: unused by generate_resp_pay; it carried invented values N/A / 8999 / MSWI0000000. NOTE: this generic error has no ReqPay to echo (request unreadable), so its Txn / Ref stay empty or N/A
+      # OLD:       payee_ref: %{
+      # OLD:         addr: "N/A",
+      # OLD:         code: "8999",
+      # OLD:         seq_num: "1",
+      # OLD:         type: "PAYEE",
+      # OLD:         ifsc: "MSWI0000000",
+      # OLD:         ac_num: "N/A",
+      # OLD:         acc_type: "CURRENT",
+      # OLD:         reg_name: "N/A",
+      # OLD:         org_amount: "0.00",
+      # OLD:         resp_code: err_code,
+      # OLD:         sett_amount: "0.00",
+      # OLD:         sett_currency: "INR"
+      # OLD:       }
     }
 
-    {:ok, xml} = UpiXmlSchema.generate_credit_response(response_data)
+    # [RespPay generate_credit_response] CHANGED: caller moved to generate_resp_pay (the old builder had no <Resp> open tag, no Ref values from ReqPay and dummy 8999 / MSWI0000000 defaults)
+    # TSD Table 4: RespPay <Resp> carries only <Ref> built from ReqPay
+    # OLD:     {:ok, xml} = UpiXmlSchema.generate_credit_response(response_data)
+    {:ok, xml} = UpiXmlSchema.generate_resp_pay(response_data)
 
     conn
     |> put_resp_content_type("application/xml")
diff --git a/apps/upi_static/lib/upi_static/error_handlers/static_qr_yh_error_handler.ex b/apps/upi_static/lib/upi_static/error_handlers/static_qr_yh_error_handler.ex
index e5547b8..2fe1ecd 100644
--- a/apps/upi_static/lib/upi_static/error_handlers/static_qr_yh_error_handler.ex
+++ b/apps/upi_static/lib/upi_static/error_handlers/static_qr_yh_error_handler.ex
@@ -217,8 +217,11 @@ defmodule UpiStatic.ErrorHandlers.StaticQrYhErrorHandler do
     Logger.info("[StaticQR-YH] Request Msg ID: #{parsed_data.msg_id}")
     Logger.info("[StaticQR-YH] Transaction ID: #{parsed_data.txn_id}")
 
-    qr_ver = Map.get(parsed_data, :qr_ver, "01")
-    qr_medium = Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03")
+    # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (01 / 03); generate_resp_pay echoes the ReqPay QR element
+    # OLD:     qr_ver = Map.get(parsed_data, :qr_ver, "01")
+    # OLD:     qr_medium = Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03")
+    qr_ver = Map.get(parsed_data, :qr_ver)
+    qr_medium = Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, Map.get(parsed_data, :qr_medium))
     expire_ts = Map.get(parsed_data, :expire_ts)
 
     payee_addr = Map.get(parsed_data, :payee_addr, "")
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 74d03b5..4248cb1 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
@@ -313,8 +313,11 @@ defmodule UpiStatic.SpecialHandlers.StaticAccIp05Scenario do
       ref_id: Map.get(parsed_data, :ref_id, ""),
       ref_url: "https://mercurypay.aritic.app",
       expire_ts: Map.get(parsed_data, :expire_ts),
-      qr_ver: Map.get(parsed_data, :qr_ver, "02"),
-      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (02 / 03); generate_resp_pay echoes the ReqPay QR element
+      # OLD:       qr_ver: Map.get(parsed_data, :qr_ver, "02"),
+      # OLD:       qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, "03"),
+      qr_ver: Map.get(parsed_data, :qr_ver),
+      qr_medium: Map.get(parsed_data, :qr_data, %{}) |> Map.get(:medium, Map.get(parsed_data, :qr_medium)),
       sp_risk_score: "0",
       npci_risk_score: Map.get(parsed_data, :npci_risk_score, "0"),
 
diff --git a/apps/upi_static/lib/upi_static/special_handlers/static_pe_error_scenario.ex b/apps/upi_static/lib/upi_static/special_handlers/static_pe_error_scenario.ex
index bb470a4..dc62d87 100644
--- a/apps/upi_static/lib/upi_static/special_handlers/static_pe_error_scenario.ex
+++ b/apps/upi_static/lib/upi_static/special_handlers/static_pe_error_scenario.ex
@@ -368,8 +368,11 @@ defmodule UpiStatic.SpecialHandlers.StaticPeErrorScenario do
         |> Keyword.get(:heartbeat_ref_url, Map.get(parsed_data, :ref_url, ""))
       ),
       expire_ts: qr_expire_ts,
-      qr_ver: Map.get(parsed_data, :qr_ver, "02"),
-      qr_medium: Map.get(parsed_data, :qr_medium, "03")
+      # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (02 / 03); generate_resp_pay echoes the ReqPay QR element
+      # OLD:       qr_ver: Map.get(parsed_data, :qr_ver, "02"),
+      # OLD:       qr_medium: Map.get(parsed_data, :qr_medium, "03")
+      qr_ver: Map.get(parsed_data, :qr_ver),
+      qr_medium: Map.get(parsed_data, :qr_medium)
     }
 
     # If QR payload contains explicit base amount/currency (bAm/bCurr), preserve verbatim.
diff --git a/apps/upi_static/lib/upi_static/static_qr_transaction_manager.ex b/apps/upi_static/lib/upi_static/static_qr_transaction_manager.ex
index 8cfb9da..ab3e21f 100644
--- a/apps/upi_static/lib/upi_static/static_qr_transaction_manager.ex
+++ b/apps/upi_static/lib/upi_static/static_qr_transaction_manager.ex
@@ -1581,16 +1581,22 @@ defmodule UpiStatic.StaticQrTransactionManager do
     org_txn_id = parsed_data.org_txn_id || txn_id
 
     # Extract QR metadata from parsed_data.qr_data (from ReqPay <QR> element)
+    # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (01); generate_resp_pay echoes the ReqPay QR element
+    # OLD:     qr_ver = get_in(parsed_data, [:qr_data, :ver]) ||
+    # OLD:              Map.get(parsed_data, :qr_ver) ||
+    # OLD:              "01"  # Default to "01" for static QR
     qr_ver = get_in(parsed_data, [:qr_data, :ver]) ||
-             Map.get(parsed_data, :qr_ver) ||
-             "01"  # Default to "01" for static QR
+             Map.get(parsed_data, :qr_ver)
 
     qr_expire_ts = get_in(parsed_data, [:qr_data, :expire_ts]) ||
                    Map.get(parsed_data, :expire_ts)
 
+    # [RespPay QR.qVer / QR.qrMedium] CHANGED: no invented default (03); generate_resp_pay echoes the ReqPay QR element
+    # OLD:     qr_medium = get_in(parsed_data, [:qr_data, :medium]) ||
+    # OLD:                 Map.get(parsed_data, :qr_medium) ||
+    # OLD:                 "03"
     qr_medium = get_in(parsed_data, [:qr_data, :medium]) ||
-                Map.get(parsed_data, :qr_medium) ||
-                "03"
+                Map.get(parsed_data, :qr_medium)
 
     Logger.info("[StaticQR-RespPay] Using QR metadata - qVer: #{qr_ver}, expireTs: #{inspect(qr_expire_ts)}, medium: #{qr_medium}")
 
@@ -1610,20 +1616,30 @@ defmodule UpiStatic.StaticQrTransactionManager do
       sub_type: Map.get(parsed_data, :sub_type, "PAY"),
       payee_addr: Map.get(parsed_data, :payee_addr, ""),
       payee_name: Map.get(parsed_data, :payee_name, ""),
-      payee_code: Map.get(parsed_data, :payee_code, "0000"),
+      # [RespPay Ref.code] CHANGED: no invented MCC 0000
+      # OLD:       payee_code: Map.get(parsed_data, :payee_code, "0000"),
+      payee_code: Map.get(parsed_data, :payee_code),
       org_amount: Map.get(processed_data, :settlement_amount, Map.get(parsed_data, :amount, "0.00")),
       resp_code: err_code,
       reg_name: fetch_reg_name_from_merchant(Map.get(parsed_data, :payee_addr), Map.get(parsed_data, :payee_name)),
       ifsc: Map.get(parsed_data, :payee_ifsc, ""),
       ac_num: Map.get(parsed_data, :payee_ac_num, ""),
-      acc_type: Map.get(parsed_data, :payee_ac_type, "CURRENT"),
+      # [RespPay Ref.accType] CHANGED: no invented CURRENT
+      # OLD:       acc_type: Map.get(parsed_data, :payee_ac_type, "CURRENT"),
+      acc_type: Map.get(parsed_data, :payee_ac_type),
       approval_num: generate_approval_number(),
       sett_amount: if(result == "SUCCESS", do: Map.get(processed_data, :settlement_amount, Map.get(parsed_data, :amount, "0.00")), else: "0.00"),
-      sett_currency: Map.get(parsed_data, :currency, "INR"),
+      # [RespPay Ref.settCurrency] CHANGED: always INR (was the ReqPay currency)
+      # TSD Table 4: settCurrency = INR
+      # OLD:       sett_currency: Map.get(parsed_data, :currency, "INR"),
+      sett_currency: "INR",
       # QR metadata from original ReqPay
       expire_ts: qr_expire_ts,
       qr_ver: qr_ver,
-      qr_medium: qr_medium
+      # [RespPay request_data] ADDED: generate_payment_response now passes the ReqPay (TSD Table 4: Txn / QR / Ref echo ReqPay)
+      # OLD:       qr_medium: qr_medium
+      qr_medium: qr_medium,
+      request_data: parsed_data
     }
 
     case UpiXmlSchema.generate_resp_pay(response_data) do
