defmodule DaProductApp.PaymentMethods do @moduledoc """ Payment Methods context for managing MPGS payment methods. This context provides secure management of payment method information with PCI compliance considerations and lifecycle management. """ import Ecto.Query, warn: false alias DaProductApp.Repo alias DaProductApp.Acquirer.Schemas.MpgsPaymentMethod @doc """ Gets a payment method by ID. """ def get_payment_method(id) do Repo.get(MpgsPaymentMethod, id) end @doc """ Gets a payment method by PCI token. """ def get_payment_method_by_pci_token(pci_token) do Repo.get_by(MpgsPaymentMethod, pci_token: pci_token) end @doc """ Gets a payment method by gateway token. """ def get_payment_method_by_gateway_token(gateway_token) do Repo.get_by(MpgsPaymentMethod, gateway_token: gateway_token) end @doc """ Creates a payment method. """ def create_payment_method(attrs \\ %{}) do %MpgsPaymentMethod{} |> MpgsPaymentMethod.changeset(attrs) |> Repo.insert() end @doc """ Creates a payment method from card data. """ def create_payment_method_from_card(card_attrs) do %MpgsPaymentMethod{} |> MpgsPaymentMethod.create_from_card_changeset(card_attrs) |> Repo.insert() end @doc """ Updates a payment method. """ def update_payment_method(%MpgsPaymentMethod{} = payment_method, attrs) do payment_method |> MpgsPaymentMethod.changeset(attrs) |> Repo.update() end @doc """ Updates payment method usage statistics. """ def update_payment_method_usage(%MpgsPaymentMethod{} = payment_method, success \\ true) do payment_method |> MpgsPaymentMethod.usage_changeset(success) |> Repo.update() end @doc """ Blocks a payment method. """ def block_payment_method(%MpgsPaymentMethod{} = payment_method, reason) do payment_method |> MpgsPaymentMethod.block_changeset(reason) |> Repo.update() end @doc """ Deletes a payment method. """ def delete_payment_method(%MpgsPaymentMethod{} = payment_method) do Repo.delete(payment_method) end @doc """ Returns an `%Ecto.Changeset{}` for tracking payment method changes. """ def change_payment_method(%MpgsPaymentMethod{} = payment_method, attrs \\ %{}) do MpgsPaymentMethod.changeset(payment_method, attrs) end @doc """ Lists payment methods for a customer. """ def list_payment_methods_by_customer(customer_id) do MpgsPaymentMethod |> where([pm], pm.customer_id == ^customer_id) |> where([pm], pm.status == "ACTIVE") |> order_by([pm], desc: pm.last_used_at) |> Repo.all() end @doc """ Lists payment methods for a merchant. """ def list_payment_methods_by_merchant(merchant_id) do MpgsPaymentMethod |> where([pm], pm.merchant_id == ^merchant_id) |> order_by([pm], desc: pm.inserted_at) |> Repo.all() end @doc """ Lists expired payment methods. """ def list_expired_payment_methods do current_date = Date.utc_today() current_year = current_date.year current_month = current_date.month MpgsPaymentMethod |> where([pm], pm.status == "ACTIVE") |> where([pm], pm.type == "CARD") |> where([pm], not is_nil(pm.expiry_year) and not is_nil(pm.expiry_month)) |> Repo.all() |> Enum.filter(&MpgsPaymentMethod.expired?/1) end @doc """ Cleanup expired payment methods. """ def cleanup_expired_payment_methods do expired_methods = list_expired_payment_methods() results = Enum.map(expired_methods, fn method -> update_payment_method(method, %{status: "EXPIRED"}) end) {successes, failures} = Enum.split_with(results, &match?({:ok, _}, &1)) %{ processed: length(expired_methods), succeeded: length(successes), failed: length(failures) } end @doc """ Gets payment method statistics. """ def get_payment_method_stats do from(pm in MpgsPaymentMethod, group_by: [pm.status, pm.card_brand], select: %{ status: pm.status, card_brand: pm.card_brand, count: count(pm.id), total_usage: sum(pm.usage_count), successful_usage: sum(pm.successful_usage_count) } ) |> Repo.all() end @doc """ Searches payment methods by last four digits. """ def search_payment_methods_by_last_four(last_four_digits, opts \\ []) do merchant_id = Keyword.get(opts, :merchant_id) customer_id = Keyword.get(opts, :customer_id) query = MpgsPaymentMethod |> where([pm], pm.last_four_digits == ^last_four_digits) |> where([pm], pm.status == "ACTIVE") query = if merchant_id, do: where(query, [pm], pm.merchant_id == ^merchant_id), else: query query = if customer_id, do: where(query, [pm], pm.customer_id == ^customer_id), else: query query |> order_by([pm], desc: pm.last_used_at) |> Repo.all() end @doc """ Validates payment method for transaction. """ def validate_payment_method_for_transaction(%MpgsPaymentMethod{} = payment_method) do cond do payment_method.status != "ACTIVE" -> {:error, :payment_method_inactive} MpgsPaymentMethod.expired?(payment_method) -> {:error, :payment_method_expired} payment_method.token_expiry && DateTime.compare(DateTime.utc_now(), payment_method.token_expiry) == :gt -> {:error, :token_expired} true -> {:ok, payment_method} end end end