defmodule DaProductAppWeb.Services.EventLogger do require Logger import Ecto.Query alias DaProductApp.Activity.{CustomEvents, CustomEventsLog} alias DaProductApp.Users.User alias DaProductApp.Repo alias DaProductApp.PosTerminal alias DaProductApp.Provider def store_event_and_log(event_name, params, amount_param, merchant_ref_number, refrence_id, transaction_id \\ nil, username \\ nil) do # Replace "provider" with actual provider name in event names actual_event_name = replace_provider_in_event_name(event_name, params) payload = cond do actual_event_name == "Response Received from QR Display" -> build_qr_display_event_payload(params, amount_param, merchant_ref_number, refrence_id, username) actual_event_name == "Request For Publish QR Code In MQTT" -> build_request_for_published_qr_code_payload(params, amount_param, merchant_ref_number, refrence_id, username) actual_event_name == "Response From Publish QR Code IN MQTT" -> build_response_from_publish_qr_code_payload(params, amount_param, merchant_ref_number, refrence_id, username) actual_event_name == "Response From Device Status" -> build_response_from_device_status_payload(params, amount_param, merchant_ref_number, refrence_id, username) actual_event_name == "Request For Publish Device QR" -> build_request_device_for_publish_qr_payload(params, amount_param, merchant_ref_number, refrence_id, username) actual_event_name == "Response From Publish Device QR" -> build_response_from_publish_device_qr_payload(params, amount_param, merchant_ref_number, refrence_id, username) # Provider-related events (these will have provider name replaced) String.contains?(actual_event_name, "Payment Request to") -> build_payment_request_payload(params, amount_param, merchant_ref_number, refrence_id, username) String.contains?(actual_event_name, "QR Code Received from") -> build_qr_code_received_payload(params, amount_param, merchant_ref_number, refrence_id, username) String.contains?(actual_event_name, "Status Enquiry to") -> build_status_enquiry_payload(params, amount_param, merchant_ref_number, refrence_id, username) String.contains?(actual_event_name, "Status Response from") -> build_status_response_payload(params, amount_param, merchant_ref_number, refrence_id, username) String.contains?(actual_event_name, "Notification Received from") -> build_notification_received_payload(params, amount_param, merchant_ref_number, refrence_id, username) String.contains?(actual_event_name, "Notification Response to") -> build_notification_response_payload(params, amount_param, merchant_ref_number, refrence_id, username) true -> build_event_payload(params, amount_param, merchant_ref_number, refrence_id, username) end with {:ok, event_id} <- store_custom_event(actual_event_name, payload), {:ok, log_id} <- store_custom_event_log(event_id, actual_event_name, payload, merchant_ref_number, refrence_id, transaction_id) do {:ok, event_id} else {:error, _} = err -> err end end # Function to replace "Provider" with actual provider name in event names defp replace_provider_in_event_name(event_name, params) do if String.contains?(event_name, "Provider") do provider_name = get_provider_name_from_params(params) if provider_name do String.replace(event_name, "Provider", String.capitalize(provider_name)) else event_name end else event_name end end # Function to get provider name from various sources in params defp get_provider_name_from_params(params) do cond do # Direct provider name in params Map.has_key?(params, "provider_name") -> Map.get(params, "provider_name") Map.has_key?(params, :provider_name) -> Map.get(params, :provider_name) # Get from device_id device_id = Map.get(params, "deviceId") || Map.get(params, :device_id) || Map.get(params, "device_id") -> get_provider_name_from_device_id(device_id) # Get from transaction reference or other identifiers Map.has_key?(params, "transaction_refid") or Map.has_key?(params, :transaction_refid) -> # Try to get provider name from existing transactions or other sources nil true -> nil end end # Function to get provider name from device_id defp get_provider_name_from_device_id(device_id) when is_binary(device_id) do try do query = from pt in PosTerminal, where: pt.serial_number == ^String.trim(device_id), join: p in Provider, on: p.id == pt.provider_id, select: p.name case Repo.one(query) do nil -> # Logger.warning("No provider found for device_id: #{device_id}") nil provider_name -> # Logger.debug("Found provider '#{provider_name}' for device_id: #{device_id}") provider_name end rescue error -> # Logger.error("Error getting provider name for device_id #{device_id}: #{inspect(error)}") nil end end defp get_provider_name_from_device_id(_), do: nil defp build_event_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, username: username, app_key: Map.get(params, "appKey"), account_label: Map.get(params, "accountLabel"), customer_mobile_number: Map.get(params, "customerMobileNumber"), external_ref_number: Map.get(params, "externalRefNumber"), external_ref_number_2: Map.get(params, "externalRefNumber2"), external_ref_number_4: Map.get(params, "externalRefNumber4"), external_ref_numbers: Map.get(params, "externalRefNumbers"), push_to: Map.get(params, "pushTo") } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_qr_display_event_payload(params, amount_param, merchant_ref_number, refrence_id, username) do # Logger.info("QR Display Event Params: #{inspect(params, pretty: true)}") payload = %{ amount: amount_param, status: Map.get(params, "status"), refrence_id: Map.get(params, "transaction_refid"), merchant_ref_number: Map.get(params, "mRefId"), qrCodeId: Map.get(params, "qrCodeId"), qrId: Map.get(params, "qrId"), transaction_id: Map.get(params, "transaction_id"), device_id: Map.get(params, "deviceSerial"), username: username } # Logger.info("QR Display Event Payload: #{inspect(payload, pretty: true)}") if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_request_for_published_qr_code_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, device_id: Map.get(params, :device_id), qrcode: Map.get(params, :qrcode), topic: Map.get(params, :topic), ts: Map.get(params, :ts), username: username } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_response_from_publish_qr_code_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, device_id: Map.get(params, :device_id), qrcode: Map.get(params, :qrcode), topic: Map.get(params, :topic), ts: Map.get(params, :ts), username: username, ref: Map.get(params, :ref), status: Map.get(params, :status), error: Map.get(params, :error), reason: Map.get(params, :reason) } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_response_from_device_status_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, device_id: Map.get(params, :device_id), status: Map.get(params, :status), ts: Map.get(params, :ts), username: username } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_request_device_for_publish_qr_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, device_id: Map.get(params, :device_id), ts: Map.get(params, :ts), username: username, topic: Map.get(params, :topic), qos: Map.get(params, :qos) } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_response_from_publish_device_qr_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, device_id: Map.get(params, :device_id), ts: Map.get(params, :ts), username: username, topic: Map.get(params, :topic), status: Map.get(params, :status), error: Map.get(params, :error) } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp store_custom_event(event_name, payload) do alias_value = event_name |> String.downcase() |> String.replace(" ", "_") date_added = DateTime.utc_now() # Handle nil username case username = Map.get(payload, :username) created_by_user = if username, do: username, else: "unknown_user" user = if username, do: Repo.get_by(User, email: username), else: nil created_by = if user, do: user.id, else: nil case CustomEvents.get_custom_event_by_alias(alias_value) do nil -> create_new_event(event_name, alias_value, date_added, created_by, created_by_user) existing -> {:ok, existing.id} end end defp create_new_event(event_name, alias_value, date_added, created_by, created_by_user) do attrs = %{ is_published: true, date_added: date_added, created_by: created_by, created_by_user: created_by_user, date_modified: date_added, modified_by: created_by, modified_by_user: created_by_user, name: event_name, alias: alias_value, payloads: %{} } case CustomEvents.create_custom_event(attrs) do {:ok, ce} -> {:ok, ce.id} {:error, _} -> {:error, :creation_failed} end end defp store_custom_event_log(event_id, event_name, payload, merchant_ref_number, refrence_id, transaction_id) do event_value = event_name |> String.downcase() |> String.replace(" ", "_") # Logger.info(""" # Attempting to store custom event log: # Event ID: #{inspect(event_id)} # Event Name: #{inspect(event_name)} # Event Value: #{inspect(event_value)} # Reference ID: #{inspect(refrence_id)} # Payload: #{inspect(payload, pretty: true)} # """) attrs = %{ transaction_id: transaction_id, event_name: event_name, event_value: event_value, date_added: DateTime.utc_now(), event_id: event_id, payload: payload, refrence_id: refrence_id } # Logger.info("Prepared attributes for custom event log: #{inspect(attrs, pretty: true)}") case CustomEventsLog.create_custom_event_log(attrs) do {:ok, log} -> # Logger.info("Successfully created custom event log with ID: #{inspect(log.id)}") {:ok, log.id} {:error, changeset} -> # Logger.error(""" # Failed to create custom event log: # Attributes: #{inspect(attrs, pretty: true)} # Errors: #{inspect(changeset.errors)} # """) {:error, :creation_failed} end rescue error -> # Logger.error(""" # Unexpected error in store_custom_event_log: # Error: #{inspect(error)} # Stacktrace: #{inspect(__STACKTRACE__)} # Params: # event_id: #{inspect(event_id)} # event_name: #{inspect(event_name)} # refrence_id: #{inspect(refrence_id)} # """) {:error, :creation_failed} end # Payload builders for QR transaction flow events defp build_payment_request_payload(params, amount_param, merchant_ref_number, refrence_id, username) do # Try to parse the request_body from params if present full_request_body = case Map.get(params, "request_body") do nil -> nil body when is_binary(body) -> case Jason.decode(body) do {:ok, parsed} -> parsed _ -> body end body -> body end payload = %{ amount: amount_param, provider_name: Map.get(params, "provider_name"), transaction_refid: Map.get(params, "transaction_refid"), merchant_id: Map.get(params, "merchant_id"), m_ref_num: Map.get(params, "m_ref_num"), username: username, request_body: full_request_body } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_qr_code_received_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, provider_name: Map.get(params, "provider_name"), transaction_refid: Map.get(params, "transaction_refid"), qr_id: Map.get(params, "qr_id"), qr_code_id: Map.get(params, "qr_code_id"), response: Map.get(params, "response"), username: username } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_status_enquiry_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, provider_name: Map.get(params, "provider_name"), payment_id: Map.get(params, "payment_id"), qr_code_id: Map.get(params, "qr_code_id"), m_ref_num: Map.get(params, "m_ref_num"), iteration: Map.get(params, "iteration"), username: username } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_status_response_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, provider_name: Map.get(params, "provider_name"), payment_id: Map.get(params, "payment_id"), qr_code_id: Map.get(params, "qr_code_id"), m_ref_num: Map.get(params, "m_ref_num"), response: Map.get(params, "response"), iteration: Map.get(params, "iteration"), username: username } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_notification_received_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, provider_name: Map.get(params, "provider_name"), notification_type: Map.get(params, "notification_type"), payment_id: Map.get(params, "payment_id"), qr_code_id: Map.get(params, "qr_code_id"), m_ref_num: Map.get(params, "m_ref_num"), notification_data: Map.get(params, "notification_data"), status: Map.get(params, "status"), transaction_status: Map.get(params, "transaction_status"), username: username } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end defp build_notification_response_payload(params, amount_param, merchant_ref_number, refrence_id, username) do payload = %{ amount: amount_param, provider_name: Map.get(params, "provider_name"), notification_type: Map.get(params, "notification_type"), payment_id: Map.get(params, "payment_id"), qr_code_id: Map.get(params, "qr_code_id"), m_ref_num: Map.get(params, "m_ref_num"), response_data: Map.get(params, "response_data"), response_status: Map.get(params, "response_status"), response_message: Map.get(params, "response_message"), response_code: Map.get(params, "response_code"), username: username } if merchant_ref_number, do: Map.put(payload, :merchant_ref_number, merchant_ref_number), else: payload end end