defmodule DaProductAppWeb.QRController do use DaProductAppWeb, :controller require Logger #@default_provider "aani" @default_provider "alipay" def generate(conn, %{"device_id" => device_id, "amount" => amount_param} = params) do provider_key = Map.get(params, "provider", @default_provider) Logger.info("QR code request for device=#{device_id} amount=#{amount_param} via #{provider_key}") amount = parse_amount(conn, amount_param) with {:ok, mod} <- DaProductApp.QRProviderFactory.fetch(provider_key), {:ok, qrcode} <- mod.generate(device_id, amount), true <- DaProductApp.DeviceRegistry.is_online?(device_id) do topic = "cmd/qr-device/#{device_id}" payload = Jason.encode!(%{amount: amount, qrcode: qrcode, ts: System.system_time(:second)}) Logger.info("Publishing to #{topic}: #{payload}") case Tortoise.publish("phoenix_client", topic, payload, qos: 1) do {:ok, _ref} -> conn |> put_status(:ok) |> json(%{status: "sent", device: device_id}) {:error, reason} -> Logger.error("Publish failed: #{inspect(reason)}") conn |> put_status(:service_unavailable) |> json(%{error: "publish_failed"}) end else {:error, :unknown_provider} -> conn |> put_status(:bad_request) |> json(%{error: "unsupported_provider"}) {:error, reason} -> conn |> put_status(:internal_server_error) |> json(%{error: inspect(reason)}) false -> conn |> put_status(:not_found) |> json(%{error: "device_offline"}) end end defp parse_amount(conn, amount) when is_integer(amount) or is_float(amount), do: amount defp parse_amount(conn, amount) when is_binary(amount) do case Integer.parse(amount) do {int, ""} -> int _ -> case Float.parse(amount) do {flt, ""} -> flt _ -> conn |> put_status(:bad_request) |> json(%{error: "invalid_amount"}) |> halt() end end end defp parse_amount(conn, _amount) do conn |> put_status(:bad_request) |> json(%{error: "invalid_amount"}) |> halt() end # Handles POST /alipay/notify def payment_notify(conn, %{"paymentResult" => %{"resultStatus" => status}}) do case status do "S" -> conn |> put_resp_content_type("application/json") |> json(%{result: "S"}) "F" -> conn |> put_resp_content_type("application/json") |> json(%{result: "F"}) _ -> conn |> put_status(:bad_request) |> put_resp_content_type("application/json") |> json(%{result: "F", error: "Unknown result status"}) end end # Handles invalid/missing payloads def payment_notify(conn, _params) do conn |> put_status(:bad_request) |> put_resp_content_type("application/json") |> json(%{result: "F", error: "Invalid payload"}) end end