cover/Elixir.WalletIntegrations.Commands.CancelPayment.html

1 defmodule WalletIntegrations.Commands.CancelPayment do
2 @moduledoc """
3 Enqueue a cancel request for a pending payment at the provider.
4
5 The PaymentRequest must not be in a terminal state. The actual cancellation
6 dispatch is handled asynchronously by PaymentWorker.
7
8 Returns `{:ok, PaymentRequest.t()}` (pre-cancel state) 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(), opts :: keyword()) ::
16 {:ok, PaymentRequest.t()} | {:error, term()}
17 4 def execute(request_id, opts \\ []) do
18 2 correlation_id =
19 Keyword.get(opts, :correlation_id, WalletSharedKernel.Correlation.new_correlation_id())
20
21 2 with {:ok, request} <- PaymentRequestStore.get(request_id),
22 2 false <- PaymentRequest.terminal?(request),
23 1 {:ok, _job_id} <-
24 JobQueue.enqueue(:integrations_payment, PaymentWorker, %{
25 "request_id" => request_id,
26 "operation" => "cancel",
27 "correlation_id" => correlation_id
28 }) do
29 1 emit_audit(request_id, correlation_id, :success, %{provider: request.provider})
30 {:ok, request}
31 else
32 1 true -> {:error, :payment_already_terminal}
33
:-(
err -> err
34 end
35 rescue
36
:-(
error -> {:error, error}
37 end
38
39 1 defp emit_audit(request_id, correlation_id, outcome, metadata) do
40 1 audit =
41 AuditEvent.build(:integrations, "cancel_payment", "payment_request", request_id,
42 outcome,
43 correlation_id: correlation_id,
44 metadata: metadata
45 )
46
47 1 :telemetry.execute([:wallet_integrations, :audit], %{}, audit)
48 rescue
49
:-(
_ -> :ok
50 end
51 end
Line Hits Source