cover/Elixir.WalletIntegrations.AdapterResult.html

1 defmodule WalletIntegrations.AdapterResult do
2 @moduledoc """
3 Normalized result model returned by all provider adapters (ADR 0008).
4
5 Fields:
6 - `status` — normalized outcome: accepted | pending | completed | failed | unknown.
7 - `provider_reference` — provider-assigned identifier for the payment/transaction.
8 - `provider_status_code` — raw provider status code or reason string.
9 - `retryable` — true when the failure is transient and safe to retry.
10 - `raw_response_hash` — SHA256 hex of raw provider response body for audit traceability.
11 - `occurred_at` — timestamp of the response (UTC).
12 - `metadata` — provider-specific fields not part of the normalized contract.
13 """
14
15 @valid_statuses [:accepted, :pending, :completed, :failed, :unknown]
16
17 @enforce_keys [:status, :occurred_at]
18 8 defstruct [
19 :status,
20 :provider_reference,
21 :provider_status_code,
22 :raw_response_hash,
23 :occurred_at,
24 retryable: false,
25 metadata: %{}
26 ]
27
28 @type status :: :accepted | :pending | :completed | :failed | :unknown
29
30 @type t :: %__MODULE__{
31 status: status(),
32 provider_reference: String.t() | nil,
33 provider_status_code: String.t() | nil,
34 retryable: boolean(),
35 raw_response_hash: String.t() | nil,
36 occurred_at: DateTime.t(),
37 metadata: map()
38 }
39
40 @doc "Build a normalized result."
41 @spec build(status(), keyword()) :: t()
42 13 def build(status, opts \\ []) when status in @valid_statuses do
43 48 %__MODULE__{
44 status: status,
45 provider_reference: Keyword.get(opts, :provider_reference),
46 provider_status_code: Keyword.get(opts, :provider_status_code),
47 retryable: Keyword.get(opts, :retryable, false),
48 raw_response_hash: Keyword.get(opts, :raw_response_hash),
49 occurred_at: Keyword.get(opts, :occurred_at, DateTime.utc_now()),
50 metadata: Keyword.get(opts, :metadata, %{})
51 }
52 end
53
54 @doc "Hash a raw response body for audit traceability. Returns hex-encoded SHA256."
55 @spec hash_response(body :: String.t() | binary()) :: String.t()
56 def hash_response(body) do
57 40 :crypto.hash(:sha256, body) |> Base.encode16(case: :lower)
58 end
59
60 @doc "True when outcome is terminal and successful."
61 @spec success?(t()) :: boolean()
62 5 def success?(%__MODULE__{status: s}), do: s in [:accepted, :completed]
63
64 @doc "True when outcome is terminal and failed."
65 @spec failed?(t()) :: boolean()
66
:-(
def failed?(%__MODULE__{status: :failed}), do: true
67
:-(
def failed?(%__MODULE__{}), do: false
68
69 @doc "True when outcome requires reconciliation review."
70 @spec needs_reconciliation?(t()) :: boolean()
71 1 def needs_reconciliation?(%__MODULE__{status: :unknown}), do: true
72 2 def needs_reconciliation?(%__MODULE__{}), do: false
73 end
Line Hits Source