cover/Elixir.WalletIntegrations.PaymentRequestStore.html

1 defmodule WalletIntegrations.PaymentRequestStore do
2 @moduledoc """
3 ETS-backed GenServer for PaymentRequest persistence.
4
5 Tables:
6 - `:wallet_integrations_pay_requests` — keyed by request_id.
7 - `:wallet_integrations_pay_req_transfer_idx` — bag: {transfer_id, request_id}.
8 - `:wallet_integrations_pay_req_prov_idx` — set: {provider_reference, request_id} for callback lookup.
9 """
10
11 use GenServer
12
13 alias WalletIntegrations.PaymentRequest
14
15 @table :wallet_integrations_pay_requests
16 @xfer_idx :wallet_integrations_pay_req_transfer_idx
17 @prov_ref_idx :wallet_integrations_pay_req_prov_idx
18
19
:-(
def start_link(opts), do: GenServer.start_link(__MODULE__, opts, name: __MODULE__)
20
21 @spec store(PaymentRequest.t()) :: :ok | {:error, :duplicate_request_id}
22 21 def store(%PaymentRequest{} = pr), do: GenServer.call(__MODULE__, {:store, pr})
23
24 @spec get(request_id :: String.t()) :: {:ok, PaymentRequest.t()} | {:error, :not_found}
25 37 def get(id), do: GenServer.call(__MODULE__, {:get, id})
26
27 @spec update(PaymentRequest.t()) :: :ok | {:error, :not_found}
28 36 def update(%PaymentRequest{} = pr), do: GenServer.call(__MODULE__, {:update, pr})
29
30 @spec get_by_provider_ref(provider_ref :: String.t()) :: {:ok, PaymentRequest.t()} | {:error, :not_found}
31
:-(
def get_by_provider_ref(ref), do: GenServer.call(__MODULE__, {:get_by_prov_ref, ref})
32
33 @spec list_by_transfer(transfer_id :: String.t()) :: [PaymentRequest.t()]
34
:-(
def list_by_transfer(transfer_id), do: GenServer.call(__MODULE__, {:list_by_transfer, transfer_id})
35
36 @spec reset() :: :ok
37 68 def reset, do: GenServer.call(__MODULE__, :reset)
38
39 @impl true
40 def init(_opts) do
41
:-(
:ets.new(@table, [:set, :protected, :named_table])
42
:-(
:ets.new(@xfer_idx, [:bag, :protected, :named_table])
43
:-(
:ets.new(@prov_ref_idx, [:set, :protected, :named_table])
44 {:ok, %{}}
45 end
46
47 @impl true
48 def handle_call({:store, pr}, _from, state) do
49 21 if :ets.member(@table, pr.request_id) do
50
:-(
{:reply, {:error, :duplicate_request_id}, state}
51 else
52 21 :ets.insert(@table, {pr.request_id, pr})
53 21 :ets.insert(@xfer_idx, {pr.transfer_id, pr.request_id})
54 21 if pr.provider_reference do
55
:-(
:ets.insert(@prov_ref_idx, {pr.provider_reference, pr.request_id})
56 end
57 21 {:reply, :ok, state}
58 end
59 end
60
61 @impl true
62 def handle_call({:get, id}, _from, state) do
63 37 result =
64 case :ets.lookup(@table, id) do
65 35 [{_, pr}] -> {:ok, pr}
66 2 [] -> {:error, :not_found}
67 end
68 37 {:reply, result, state}
69 end
70
71 @impl true
72 def handle_call({:update, pr}, _from, state) do
73 36 result =
74 36 case :ets.lookup(@table, pr.request_id) do
75 [{_, old}] ->
76 36 :ets.insert(@table, {pr.request_id, pr})
77 # Update provider_reference index if changed
78 36 if old.provider_reference != pr.provider_reference do
79 12 if old.provider_reference, do: :ets.delete(@prov_ref_idx, old.provider_reference)
80 12 if pr.provider_reference, do: :ets.insert(@prov_ref_idx, {pr.provider_reference, pr.request_id})
81 end
82 :ok
83
:-(
[] ->
84 {:error, :not_found}
85 end
86 36 {:reply, result, state}
87 end
88
89 @impl true
90 def handle_call({:get_by_prov_ref, ref}, _from, state) do
91
:-(
result =
92 case :ets.lookup(@prov_ref_idx, ref) do
93 [{_, req_id}] ->
94
:-(
case :ets.lookup(@table, req_id) do
95
:-(
[{_, pr}] -> {:ok, pr}
96
:-(
[] -> {:error, :not_found}
97 end
98
:-(
[] -> {:error, :not_found}
99 end
100
:-(
{:reply, result, state}
101 end
102
103 @impl true
104 def handle_call({:list_by_transfer, transfer_id}, _from, state) do
105
:-(
ids = :ets.lookup(@xfer_idx, transfer_id) |> Enum.map(fn {_, id} -> id end)
106
:-(
items = Enum.flat_map(ids, fn id ->
107
:-(
case :ets.lookup(@table, id) do
108
:-(
[{_, pr}] -> [pr]
109
:-(
[] -> []
110 end
111 end)
112
:-(
{:reply, items, state}
113 end
114
115 @impl true
116 def handle_call(:reset, _from, state) do
117 68 :ets.delete_all_objects(@table)
118 68 :ets.delete_all_objects(@xfer_idx)
119 68 :ets.delete_all_objects(@prov_ref_idx)
120 68 {:reply, :ok, state}
121 end
122 end
Line Hits Source