| 1 |
|
defmodule WalletLimitsFees.Commands.UpsertLimitPolicy do |
| 2 |
|
@moduledoc """ |
| 3 |
|
Command handler for creating or updating a limit policy. |
| 4 |
|
|
| 5 |
|
If a policy already exists for the given tier/currency pair, it is versioned |
| 6 |
|
and updated. Otherwise a new policy is created. |
| 7 |
|
""" |
| 8 |
|
|
| 9 |
|
alias WalletLimitsFees.{LimitPolicy, PolicyStore} |
| 10 |
|
alias WalletLimitsFees.Events.LimitPolicyUpdated |
| 11 |
|
alias WalletObservability.AuditEvent |
| 12 |
|
|
| 13 |
|
@doc """ |
| 14 |
|
Executes the upsert limit policy command. |
| 15 |
|
|
| 16 |
|
Params map keys: |
| 17 |
|
- `:tier` — `:standard | :premium | :business` |
| 18 |
|
- `:currency` — ISO 4217 currency code string |
| 19 |
|
- `:max_transaction_amount` — maximum single transaction amount (minor units) |
| 20 |
|
- `:daily_limit` — maximum daily spend (minor units) |
| 21 |
|
- `:monthly_limit` — maximum monthly spend (minor units) |
| 22 |
|
- `:correlation_id` — optional correlation ID |
| 23 |
|
|
| 24 |
|
Returns `{:ok, policy}` or `{:error, reason}`. |
| 25 |
|
""" |
| 26 |
|
@spec execute(params :: map()) :: {:ok, LimitPolicy.t()} | {:error, term()} |
| 27 |
7 |
def execute(params) do |
| 28 |
7 |
tier = Map.fetch!(params, :tier) |
| 29 |
7 |
currency = Map.fetch!(params, :currency) |
| 30 |
7 |
correlation_id = |
| 31 |
|
Map.get(params, :correlation_id, WalletSharedKernel.Correlation.new_correlation_id()) |
| 32 |
|
|
| 33 |
7 |
policy = |
| 34 |
|
case PolicyStore.get_limit_policy(tier, currency) do |
| 35 |
|
{:error, :not_found} -> |
| 36 |
6 |
LimitPolicy.new( |
| 37 |
|
tier, |
| 38 |
|
currency, |
| 39 |
|
Map.fetch!(params, :max_transaction_amount), |
| 40 |
|
Map.fetch!(params, :daily_limit), |
| 41 |
|
Map.fetch!(params, :monthly_limit) |
| 42 |
|
) |
| 43 |
|
|
| 44 |
|
{:ok, existing} -> |
| 45 |
1 |
LimitPolicy.bump_version(existing, params) |
| 46 |
|
end |
| 47 |
|
|
| 48 |
7 |
:ok = PolicyStore.store_limit_policy(policy) |
| 49 |
|
|
| 50 |
7 |
emit_event( |
| 51 |
7 |
LimitPolicyUpdated.build(policy.policy_id, tier, currency, |
| 52 |
|
correlation_id: correlation_id, |
| 53 |
7 |
version: policy.version |
| 54 |
|
) |
| 55 |
|
) |
| 56 |
|
|
| 57 |
7 |
audit = |
| 58 |
7 |
AuditEvent.build(:policy, "limit_policy_upserted", "policy", policy.policy_id, :success, |
| 59 |
|
correlation_id: correlation_id, |
| 60 |
7 |
metadata: %{tier: tier, currency: currency, version: policy.version} |
| 61 |
|
) |
| 62 |
|
|
| 63 |
7 |
:telemetry.execute([:wallet_limits_fees, :audit], %{}, audit) |
| 64 |
|
|
| 65 |
|
{:ok, policy} |
| 66 |
|
rescue |
| 67 |
:-( |
error -> {:error, error} |
| 68 |
|
end |
| 69 |
|
|
| 70 |
7 |
defp emit_event(event) do |
| 71 |
7 |
pubsub = Application.get_env(:wallet_limits_fees, :pubsub, WalletWeb.PubSub) |
| 72 |
7 |
apply(Phoenix.PubSub, :broadcast, [pubsub, "wallet_limits_fees:events", {:domain_event, event}]) |
| 73 |
|
rescue |
| 74 |
:-( |
_ -> :ok |
| 75 |
|
end |
| 76 |
|
end |