| 1 |
|
defmodule WalletLimitsFees.Commands.UpsertFeePolicy do |
| 2 |
|
@moduledoc """ |
| 3 |
|
Command handler for creating or updating a fee policy. |
| 4 |
|
|
| 5 |
|
If a policy already exists for the given tier/currency/transfer_type combination, |
| 6 |
|
it is versioned and updated. Otherwise a new policy is created. |
| 7 |
|
""" |
| 8 |
|
|
| 9 |
|
alias WalletLimitsFees.{FeePolicy, PolicyStore} |
| 10 |
|
alias WalletLimitsFees.Events.FeePolicyUpdated |
| 11 |
|
alias WalletObservability.AuditEvent |
| 12 |
|
|
| 13 |
|
@doc """ |
| 14 |
|
Executes the upsert fee policy command. |
| 15 |
|
|
| 16 |
|
Params map keys: |
| 17 |
|
- `:tier` — `:standard | :premium | :business` |
| 18 |
|
- `:currency` — ISO 4217 currency code string |
| 19 |
|
- `:transfer_type` — `:internal | :p2p | :external` |
| 20 |
|
- `:flat_fee` — flat fee in minor units (default 0) |
| 21 |
|
- `:percentage_fee` — fraction e.g. 0.015 = 1.5% (default 0.0) |
| 22 |
|
- `:min_fee` — minimum fee floor in minor units (default 0) |
| 23 |
|
- `:max_fee` — maximum fee ceiling; 0 means no ceiling (default 0) |
| 24 |
|
- `:correlation_id` — optional correlation ID |
| 25 |
|
|
| 26 |
|
Returns `{:ok, policy}` or `{:error, reason}`. |
| 27 |
|
""" |
| 28 |
|
@spec execute(params :: map()) :: {:ok, FeePolicy.t()} | {:error, term()} |
| 29 |
7 |
def execute(params) do |
| 30 |
7 |
tier = Map.fetch!(params, :tier) |
| 31 |
7 |
currency = Map.fetch!(params, :currency) |
| 32 |
7 |
transfer_type = Map.fetch!(params, :transfer_type) |
| 33 |
7 |
correlation_id = |
| 34 |
|
Map.get(params, :correlation_id, WalletSharedKernel.Correlation.new_correlation_id()) |
| 35 |
|
|
| 36 |
7 |
policy = |
| 37 |
|
case PolicyStore.get_fee_policy(tier, currency, transfer_type) do |
| 38 |
|
{:error, :not_found} -> |
| 39 |
6 |
FeePolicy.new(tier, currency, transfer_type, params) |
| 40 |
|
|
| 41 |
|
{:ok, existing} -> |
| 42 |
1 |
FeePolicy.bump_version(existing, params) |
| 43 |
|
end |
| 44 |
|
|
| 45 |
7 |
:ok = PolicyStore.store_fee_policy(policy) |
| 46 |
|
|
| 47 |
7 |
emit_event( |
| 48 |
7 |
FeePolicyUpdated.build(policy.policy_id, tier, currency, transfer_type, |
| 49 |
|
correlation_id: correlation_id, |
| 50 |
7 |
version: policy.version |
| 51 |
|
) |
| 52 |
|
) |
| 53 |
|
|
| 54 |
7 |
audit = |
| 55 |
7 |
AuditEvent.build(:policy, "fee_policy_upserted", "policy", policy.policy_id, :success, |
| 56 |
|
correlation_id: correlation_id, |
| 57 |
7 |
metadata: %{tier: tier, currency: currency, transfer_type: transfer_type, version: policy.version} |
| 58 |
|
) |
| 59 |
|
|
| 60 |
7 |
:telemetry.execute([:wallet_limits_fees, :audit], %{}, audit) |
| 61 |
|
|
| 62 |
|
{:ok, policy} |
| 63 |
|
rescue |
| 64 |
:-( |
error -> {:error, error} |
| 65 |
|
end |
| 66 |
|
|
| 67 |
7 |
defp emit_event(event) do |
| 68 |
7 |
pubsub = Application.get_env(:wallet_limits_fees, :pubsub, WalletWeb.PubSub) |
| 69 |
7 |
apply(Phoenix.PubSub, :broadcast, [pubsub, "wallet_limits_fees:events", {:domain_event, event}]) |
| 70 |
|
rescue |
| 71 |
:-( |
_ -> :ok |
| 72 |
|
end |
| 73 |
|
end |