cover/Elixir.WalletIntegrations.Commands.RefundPayment.html

1 defmodule WalletIntegrations.Commands.RefundPayment do
2 @moduledoc """
3 Enqueue a refund for a completed payment at the provider.
4
5 The PaymentRequest must be in `:completed` status. The refund amount
6 is in minor currency units. Actual refund dispatch is asynchronous.
7
8 Returns `{:ok, PaymentRequest.t()}` or `{:error, reason}`.
9 """
10
11 alias WalletIntegrations.{PaymentRequest, PaymentRequestStore, JobQueue}
12 alias WalletIntegrations.Workers.PaymentWorker
13 alias WalletObservability.AuditEvent
14
15 @spec execute(request_id :: String.t(), amount :: pos_integer(), opts :: keyword()) ::
16 {:ok, PaymentRequest.t()} | {:error, term()}
17 6 def execute(request_id, amount, opts \\ []) do
18 3 correlation_id =
19 Keyword.get(opts, :correlation_id, WalletSharedKernel.Correlation.new_correlation_id())
20
21 3 with {:ok, %PaymentRequest{status: :completed} = request} <- PaymentRequestStore.get(request_id),
22 2 true <- is_integer(amount) and amount > 0,
23 1 {:ok, _job_id} <-
24 JobQueue.enqueue(:integrations_refund, PaymentWorker, %{
25 "request_id" => request_id,
26 "operation" => "refund",
27 "refund_amount" => amount,
28 "correlation_id" => correlation_id
29 }) do
30 1 emit_audit(request_id, correlation_id, :success, %{provider: request.provider, amount: amount})
31 {:ok, request}
32 else
33 1 {:ok, %PaymentRequest{}} -> {:error, :payment_not_completed}
34 1 false -> {:error, :invalid_refund_amount}
35
:-(
err -> err
36 end
37 rescue
38
:-(
error -> {:error, error}
39 end
40
41 1 defp emit_audit(request_id, correlation_id, outcome, metadata) do
42 1 audit =
43 AuditEvent.build(:integrations, "refund_payment", "payment_request", request_id,
44 outcome,
45 correlation_id: correlation_id,
46 metadata: metadata
47 )
48
49 1 :telemetry.execute([:wallet_integrations, :audit], %{}, audit)
50 rescue
51
:-(
_ -> :ok
52 end
53 end
Line Hits Source