defmodule DaProductAppWeb.QRInitiateController do use DaProductAppWeb, :controller require Logger alias DaProductAppWeb.Validators.QRParamsValidator alias DaProductAppWeb.Services.EventLogger alias DaProductAppWeb.Handlers.ResponseHandler @default_provider "alipay" @spec initiate(any(), any()) :: Plug.Conn.t() def initiate(conn, params) do amount = parse_amount(params["amount"]) refrence_id = params["externalRefNumber"] with {:ok, _} <- EventLogger.store_event_and_log("Initiate QR Request", params, amount, nil, refrence_id, nil, params["username"]), :ok <- QRParamsValidator.validate_params(params), {:ok, ref} <- display_qr_code(conn, params, amount) do ResponseHandler.send_success(conn, ref) else # Add new error case for event logging {:error, :event_log} -> ResponseHandler.error_response(conn, :internal_server_error, "MERCURY_0000503", "Failed to log initiate event") {:error, msg} when is_binary(msg) -> ResponseHandler.error_response(conn, :bad_request, "MERCURY_4000001", msg) {:error, :provider_key} -> ResponseHandler.error_response(conn, :bad_request, "MERCURY_6000001", "Failed to retrieve provider key.") {:error, :event_log, code, msg} -> ResponseHandler.error_response(conn, :internal_server_error, code, msg) {:error, :unsupported_provider} -> ResponseHandler.error_response(conn, :bad_request, "MERCURY_0000000", "unsupported_provider") {:error, :qr_generation, reason} -> ResponseHandler.error_response(conn, :internal_server_error, "MERCURY_0000501", "Unexpected error: #{inspect(reason)}") {:error, :not_found, code, msg} -> ResponseHandler.error_response(conn, :service_unavailable, code, msg) {:error, :publish, reason} -> ResponseHandler.error_response(conn, :service_unavailable, "MERCURY_0000623", "Publish failed: #{inspect(reason)}") {:error, :provider_not_associated_with_device_id} -> ResponseHandler.error_response(conn, :provider_not_associated_with_device_id, "MERCURY_0000404", "Device not found") {:error, :invalid_amount} -> ResponseHandler.error_response(conn, :bad_request, "MERCURY_0000402", "Invalid amount provided") {:error, :invalid_params} -> ResponseHandler.error_response(conn, :bad_request, "MERCURY_0000400", "Invalid parameters provided") {:error, :provider_not_found} -> ResponseHandler.error_response(conn, :not_found, "MERCURY_0000405", "Provider not found") _ -> ResponseHandler.error_response(conn, :internal_server_error, "MERCURY_5000000", "Unknown error.") end end # Add this action to QRInitiateController def status(conn, params) do Logger.info("Status request params: #{inspect(params)}") ref_number = params["transaction_ref_number"] Logger.info("Transaction reference number: #{inspect(ref_number)}") if is_nil(ref_number) do response = %{ code: "MERCURY_0000400", errorCode: "Txn_Not_CMPT", messageCode: "Missing transaction reference number", status: "failed", time: DateTime.utc_now() |> DateTime.to_unix() } json(conn, response) else case DaProductApp.Transactions.Transactions.get_transaction_by_ref_number(ref_number) do nil -> response = %{ code: "MERCURY_0000404", messageCode: "Transaction not found", status: "failed", errorCode: "Txn_Not_CMPT", time: DateTime.utc_now() |> DateTime.to_unix() } json(conn, response) transaction -> response = if transaction.status == "success" do %{ code: "200", messageCode: "Transaction successful", status: "success", time: DateTime.utc_now() |> DateTime.to_unix(), data: %{ transaction_ref_number: transaction.transaction_ref_number, status: transaction.status, transaction_amount: transaction.transaction_amount, transaction_id: transaction.id, merchant_refrence_number: transaction.m_ref_num, created_at: transaction.inserted_at } } else %{ code: "300", messageCode: "Transaction not successful", status: transaction.status, time: DateTime.utc_now() |> DateTime.to_unix(), data: %{ transaction_ref_number: transaction.transaction_ref_number, status: transaction.status, transaction_amount: transaction.transaction_amount, transaction_id: transaction.id, merchant_refrence_number: transaction.m_ref_num, created_at: transaction.inserted_at } } end json(conn, response) end end end # Generates QR code, checks device online, logs event, publishes QR, sends response. defp display_qr_code(conn, params, amount) do device_id = params["pushTo"]["deviceId"] # Add event logging for QR Code request {:ok, _} = EventLogger.store_event_and_log( "Request For Get QRCode", params, amount, nil, params["externalRefNumber"], nil, params["username"] ) # Prepare parameters for QRMiddleLayerController middle_layer_params = %{ "transaction_refid" => params["externalRefNumber"], "merchantId" => params["appKey"], "merchantName" => params["accountLabel"], "amount" => amount, "additionalData" => %{ "customerMobileNumber" => params["customerMobileNumber"], "externalRefNumber2" => params["externalRefNumber2"], "externalRefNumber4" => params["externalRefNumber4"], "externalRefNumbers" => params["externalRefNumbers"] }, "deviceId" => device_id } Logger.info("QR Request - Starting process for device: #{device_id}") Logger.info("QR Request - Parameters: #{inspect(middle_layer_params)}") # Call QRMiddleLayerController's processTransaction #response = DaProductAppWeb.QRMiddleLayerController.processTransaction(conn, middle_layer_params) #Logger.info("Raw QR Middleware Response: #{inspect(response, pretty: true, limit: :infinity)}") # Replace the direct function call with HTTP request response = case HTTPoison.post( "http://demo.ctrmv.com:4004/api/processTransaction", # Replace with your actual API endpoint Jason.encode!(middle_layer_params), [{"Content-Type", "application/json"}] ) do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> case Jason.decode(body) do {:ok, decoded_response} -> {:ok, decoded_response["data"]} {:error, decode_error} -> Logger.error("Failed to decode response: #{inspect(decode_error)}") {:error, :decode_error, "Failed to decode response"} end {:ok, %HTTPoison.Response{status_code: 404, body: body}} -> case Jason.decode(body) do {:ok, %{"code" => code, "message" => message}} -> {:error, :not_found, code, message} _ -> {:error, :not_found, "MERCURY_0000404", "Resource not found"} end {:ok, %HTTPoison.Response{status_code: status_code, body: body}} -> case Jason.decode(body) do {:ok, %{"code" => code, "message" => message}} -> Logger.error("Error response with code #{code}: #{message}") {:error, :unexpected_response, code, message} _ -> Logger.error("Unexpected status code #{status_code}: #{inspect(body)}") {:error, :unexpected_response, "MERCURY_#{status_code}", "Unexpected response from server"} end {:error, %HTTPoison.Error{reason: reason}} -> Logger.error("HTTP request failed: #{inspect(reason)}") {:error, :http_error, "MERCURY_0000500", "Failed to make HTTP request: #{inspect(reason)}"} end case response do {:ok, qr_response} -> Logger.info(""" QR Success Response: - Device ID: #{device_id} - QR Code: #{inspect(qr_response["qrId"])} - Full Response: #{inspect(qr_response, pretty: true)} """) is_device_online = DaProductApp.DeviceRegistry.is_online?(device_id) Logger.info("Device Status - #{device_id} is #{if is_device_online, do: "online", else: "offline"}") qrResponse = qr_response merchantId = qrResponse["mRefId"] transaction_id = qrResponse["transaction_id"] Logger.info("Merchant ID: #{merchantId}") with true <- is_device_online, {:ok, _} <- EventLogger.store_event_and_log("Response Received from QR Display", qrResponse, amount, merchantId, params["externalRefNumber"], transaction_id, params["username"]), {:ok, _} <- DaProductApp.Activity.CustomEventsLog.update_transaction_id(params["externalRefNumber"], transaction_id), {:ok, ref} <- publish_qr(device_id, amount, qr_response,params) do Logger.info("QR Published - Successfully published QR code with ref: #{inspect(ref)}") {:ok, ref} else false -> Logger.error("Device offline error for device_id: #{device_id}") {:error, :device_offline} {:error, reason} -> Logger.error("Publish error: #{inspect(reason)}") {:error, :publish, reason} end {:error, :not_found, code, message} -> Logger.error(""" QR Error - Not Found: - Device ID: #{device_id} - Error Code: #{code} - Message: #{message} """) {:error, :not_found, code, message} {:error, reason, message} -> Logger.error(""" QR Error: - Device ID: #{device_id} - Error Type: #{reason} - Message: #{message} """) {:error, :qr_generation, message} unexpected -> Logger.error(""" QR Unexpected Error: - Device ID: #{device_id} - Response: #{inspect(unexpected)} """) {:error, :qr_generation, "Unexpected response"} end end # Publishes QR code payload to MQTT topic. defp publish_qr(device_id, amount, qr_response, params) do Logger.info("Publishing QR code then get qr code url: #{inspect(qr_response["qrCodeId"])}") # Add event logging before publishing {:ok, _} = EventLogger.store_event_and_log( "Request For Publish QR Code In MQTT", %{ device_id: device_id, amount: amount, qrcode: qr_response["qrId"], topic: "/ota/pFppbioOCKlo5c8E/#{device_id}/update", ts: System.system_time(:second) }, amount, qr_response["mRefId"], params["externalRefNumber"], qr_response["transaction_id"], params["username"] ) Logger.info("Publishing QR code to MQTT topic: cmd/qr-device/#{device_id}") #topic = "cmd/qr-device/#{device_id}" topic = "/ota/pFppbioOCKlo5c8E/#{device_id}/update" #payload = Jason.encode!(%{amount: amount, qrcode: qr_response.qrId, ts: System.system_time(:second)}) #payload = Jason.encode!(%{amount: amount, qrcode: qr_response["qrCodeId"], ts: System.system_time(:second)}) datetime = NaiveDateTime.utc_now() |> NaiveDateTime.to_iso8601() |> String.replace(~r/[-:T]/, "") |> String.slice(0..13) # Trim milliseconds to match expected format #request_id = :crypto.strong_rand_bytes(8) |> Base.encode16() request_id = qr_response["transaction_id"] payload = Jason.encode!(%{ money: amount, order_sn: qr_response["qrCodeId"], datetime: datetime, ctime: System.system_time(:second), request_id: request_id }) Logger.info(""" MQTT Publish Details: - Topic: #{topic} - Payload: #{payload} """) case Tortoise.publish("phoenix_client_node_devteammiddlelayer", topic, payload, qos: 1) do {:ok, ref} = response -> # Log successful publish response {:ok, _} = EventLogger.store_event_and_log( "Response From Publish QR Code IN MQTT", %{ device_id: device_id, amount: amount, qrcode: qr_response["qrId"], topic: topic, ref: inspect(ref), status: "sent", ts: System.system_time(:second), error: nil, reason: nil }, amount, qr_response["mRefId"], params["externalRefNumber"], qr_response["transaction_id"], params["username"] ) Logger.info(""" MQTT Publish Success: - Reference: #{inspect(ref)} - Device ID: #{device_id} - Response: #{inspect(response)} """) #{:ok, ref} {:ok, qr_response} {:error, reason} = error -> # Log failed publish response {:ok, _} = EventLogger.store_event_and_log( "Response From Publish QR Code IN MQTT", %{ device_id: device_id, amount: amount, qrcode: qr_response["qrId"], topic: topic, ref: nil, status: "unsent", ts: System.system_time(:second), error: inspect(error), reason: inspect(reason), }, amount, qr_response["mRefId"], params["externalRefNumber"], qr_response["transaction_id"], params["username"] ) Logger.error(""" MQTT Publish Error: - Device ID: #{device_id} - Error: #{inspect(error)} - Reason: #{inspect(reason)} """) {:error, reason} end end # Parses amount as integer or float, raises if invalid. defp parse_amount(amount) when is_integer(amount) or is_float(amount), do: amount defp parse_amount(amount) when is_binary(amount) do case Integer.parse(amount) do {int, ""} -> int _ -> case Float.parse(amount) do {flt, ""} -> flt _ -> raise "invalid_amount" end end end defp parse_amount(_), do: raise "invalid_amount" end