Current section
Files
Jump to
Current section
Files
lib/paysafe/types.ex
defmodule Paysafe.Types do
@moduledoc """
Shared typespecs used across all Paysafe API modules.
"""
# ── Payment Handle ──────────────────────────────────────────────────────────
defmodule PaymentHandle do
@moduledoc "Represents a Paysafe Payment Handle response."
@type status ::
:initiated
| :payable
| :processing
| :completed
| :expired
| :failed
| :cancelled
| :error
@type action :: :none | :redirect
@type execution_mode :: :synchronous | :asynchronous
@type usage :: :single_use | :multi_use
@type transaction_type ::
:payment
| :standalone_credit
| :original_credit
| :verification
@type t :: %__MODULE__{
id: String.t() | nil,
merchant_ref_num: String.t(),
amount: pos_integer(),
currency_code: String.t(),
status: status(),
payment_type: String.t(),
payment_handle_token: String.t() | nil,
action: action() | nil,
usage: usage(),
execution_mode: execution_mode() | nil,
time_to_live_seconds: non_neg_integer() | nil,
transaction_type: transaction_type(),
links: [map()] | [],
return_links: [map()] | [],
raw: map()
}
defstruct [
:id,
:merchant_ref_num,
:amount,
:currency_code,
:status,
:payment_type,
:payment_handle_token,
:action,
:usage,
:execution_mode,
:time_to_live_seconds,
:transaction_type,
links: [],
return_links: [],
raw: %{}
]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
merchant_ref_num: m["merchantRefNum"],
amount: m["amount"],
currency_code: m["currencyCode"],
status: parse_status(m["status"]),
payment_type: m["paymentType"],
payment_handle_token: m["paymentHandleToken"],
action: parse_action(m["action"]),
usage: parse_usage(m["usage"]),
execution_mode: parse_execution_mode(m["executionMode"]),
time_to_live_seconds: m["timeToLiveSeconds"],
transaction_type: parse_transaction_type(m["transactionType"]),
links: m["links"] || [],
return_links: m["returnLinks"] || [],
raw: m
}
end
defp parse_status("INITIATED"), do: :initiated
defp parse_status("PAYABLE"), do: :payable
defp parse_status("PROCESSING"), do: :processing
defp parse_status("COMPLETED"), do: :completed
defp parse_status("EXPIRED"), do: :expired
defp parse_status("FAILED"), do: :failed
defp parse_status("CANCELLED"), do: :cancelled
defp parse_status("ERROR"), do: :error
defp parse_status(s), do: s && String.downcase(s) |> String.to_atom()
defp parse_action("NONE"), do: :none
defp parse_action("REDIRECT"), do: :redirect
defp parse_action(_), do: nil
defp parse_usage("SINGLE_USE"), do: :single_use
defp parse_usage("MULTI_USE"), do: :multi_use
defp parse_usage(_), do: nil
defp parse_execution_mode("SYNCHRONOUS"), do: :synchronous
defp parse_execution_mode("ASYNCHRONOUS"), do: :asynchronous
defp parse_execution_mode(_), do: nil
defp parse_transaction_type("PAYMENT"), do: :payment
defp parse_transaction_type("STANDALONE_CREDIT"), do: :standalone_credit
defp parse_transaction_type("ORIGINAL_CREDIT"), do: :original_credit
defp parse_transaction_type("VERIFICATION"), do: :verification
defp parse_transaction_type(_), do: :payment
end
# ── Payment ─────────────────────────────────────────────────────────────────
defmodule Payment do
@moduledoc "Represents a Paysafe Payment response."
@type status :: :processing | :completed | :failed | :cancelled | :pending
@type t :: %__MODULE__{
id: String.t() | nil,
merchant_ref_num: String.t(),
amount: pos_integer(),
currency_code: String.t(),
status: status(),
settle_with_auth: boolean(),
txn_time: String.t() | nil,
card_type: String.t() | nil,
card_bin: String.t() | nil,
last_digits: String.t() | nil,
authentication: map() | nil,
settlements: [map()] | [],
error: map() | nil,
raw: map()
}
defstruct [
:id,
:merchant_ref_num,
:amount,
:currency_code,
:status,
:settle_with_auth,
:txn_time,
:card_type,
:card_bin,
:last_digits,
:authentication,
:error,
settlements: [],
raw: %{}
]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
merchant_ref_num: m["merchantRefNum"],
amount: m["amount"],
currency_code: m["currencyCode"],
status: parse_status(m["status"]),
settle_with_auth: m["settleWithAuth"],
txn_time: m["txnTime"],
card_type: m["cardType"],
card_bin: m["cardBin"],
last_digits: m["lastDigits"],
authentication: m["authentication"],
settlements: m["settlements"] || [],
error: m["error"],
raw: m
}
end
defp parse_status("PROCESSING"), do: :processing
defp parse_status("COMPLETED"), do: :completed
defp parse_status("FAILED"), do: :failed
defp parse_status("CANCELLED"), do: :cancelled
defp parse_status("PENDING"), do: :pending
defp parse_status(_), do: :processing
end
# ── Settlement ───────────────────────────────────────────────────────────────
defmodule Settlement do
@moduledoc "Represents a Paysafe Settlement response."
@type status :: :pending | :processing | :completed | :cancelled | :failed
@type t :: %__MODULE__{
id: String.t() | nil,
merchant_ref_num: String.t(),
amount: pos_integer(),
status: status(),
txn_time: String.t() | nil,
raw: map()
}
defstruct [:id, :merchant_ref_num, :amount, :status, :txn_time, raw: %{}]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
merchant_ref_num: m["merchantRefNum"],
amount: m["amount"],
status: parse_status(m["status"]),
txn_time: m["txnTime"],
raw: m
}
end
defp parse_status("PENDING"), do: :pending
defp parse_status("PROCESSING"), do: :processing
defp parse_status("COMPLETED"), do: :completed
defp parse_status("CANCELLED"), do: :cancelled
defp parse_status("FAILED"), do: :failed
defp parse_status(_), do: :pending
end
# ── Refund ───────────────────────────────────────────────────────────────────
defmodule Refund do
@moduledoc "Represents a Paysafe Refund response."
@type status :: :processing | :completed | :failed | :cancelled | :pending
@type t :: %__MODULE__{
id: String.t() | nil,
merchant_ref_num: String.t(),
amount: pos_integer(),
status: status(),
txn_time: String.t() | nil,
error: map() | nil,
raw: map()
}
defstruct [:id, :merchant_ref_num, :amount, :status, :txn_time, :error, raw: %{}]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
merchant_ref_num: m["merchantRefNum"],
amount: m["amount"],
status: parse_status(m["status"]),
txn_time: m["txnTime"],
error: m["error"],
raw: m
}
end
defp parse_status("PROCESSING"), do: :processing
defp parse_status("COMPLETED"), do: :completed
defp parse_status("FAILED"), do: :failed
defp parse_status("CANCELLED"), do: :cancelled
defp parse_status("PENDING"), do: :pending
defp parse_status(_), do: :processing
end
# ── Plan (Scheduler) ─────────────────────────────────────────────────────────
defmodule Plan do
@moduledoc "Represents a Payment Scheduler Plan."
@type billing_cycle ::
:daily | :weekly | :bi_weekly | :monthly | :quarterly | :semi_annually | :annually
@type status :: :active | :inactive | :discontinued
@type t :: %__MODULE__{
id: String.t() | nil,
name: String.t(),
amount: pos_integer(),
currency_code: String.t(),
billing_cycle: billing_cycle(),
num_payments: pos_integer() | nil,
status: status(),
raw: map()
}
defstruct [
:id,
:name,
:amount,
:currency_code,
:billing_cycle,
:num_payments,
:status,
raw: %{}
]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
name: m["name"],
amount: m["amount"],
currency_code: m["currencyCode"],
billing_cycle: parse_cycle(m["billingCycle"]),
num_payments: m["numPayments"],
status: parse_status(m["status"]),
raw: m
}
end
defp parse_cycle("DAILY"), do: :daily
defp parse_cycle("WEEKLY"), do: :weekly
defp parse_cycle("BI_WEEKLY"), do: :bi_weekly
defp parse_cycle("MONTHLY"), do: :monthly
defp parse_cycle("QUARTERLY"), do: :quarterly
defp parse_cycle("SEMI_ANNUALLY"), do: :semi_annually
defp parse_cycle("ANNUALLY"), do: :annually
defp parse_cycle(_), do: :monthly
defp parse_status("ACTIVE"), do: :active
defp parse_status("INACTIVE"), do: :inactive
defp parse_status("DISCONTINUED"), do: :discontinued
defp parse_status(_), do: :active
end
# ── Subscription (Scheduler) ─────────────────────────────────────────────────
defmodule Subscription do
@moduledoc "Represents a Payment Scheduler Subscription."
@type status ::
:active
| :inactive
| :suspended
| :cancelled
| :expired
| :future
| :completed
@type t :: %__MODULE__{
id: String.t() | nil,
plan_id: String.t(),
merchant_customer_id: String.t(),
amount: pos_integer(),
currency_code: String.t(),
status: status(),
next_payment_date: String.t() | nil,
num_payments_remaining: non_neg_integer() | nil,
raw: map()
}
defstruct [
:id,
:plan_id,
:merchant_customer_id,
:amount,
:currency_code,
:status,
:next_payment_date,
:num_payments_remaining,
raw: %{}
]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
plan_id: m["planId"],
merchant_customer_id: m["merchantCustomerId"],
amount: m["amount"],
currency_code: m["currencyCode"],
status: parse_status(m["status"]),
next_payment_date: m["nextPaymentDate"],
num_payments_remaining: m["numPaymentsRemaining"],
raw: m
}
end
defp parse_status("ACTIVE"), do: :active
defp parse_status("INACTIVE"), do: :inactive
defp parse_status("SUSPENDED"), do: :suspended
defp parse_status("CANCELLED"), do: :cancelled
defp parse_status("EXPIRED"), do: :expired
defp parse_status("FUTURE"), do: :future
defp parse_status("COMPLETED"), do: :completed
defp parse_status(_), do: :active
end
# ── Customer (Vault) ─────────────────────────────────────────────────────────
defmodule Customer do
@moduledoc "Represents a Customer Vault profile."
@type t :: %__MODULE__{
id: String.t() | nil,
merchant_customer_id: String.t(),
first_name: String.t() | nil,
last_name: String.t() | nil,
email: String.t() | nil,
phone: String.t() | nil,
locale: String.t() | nil,
cards: [map()] | [],
raw: map()
}
defstruct [
:id,
:merchant_customer_id,
:first_name,
:last_name,
:email,
:phone,
:locale,
cards: [],
raw: %{}
]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
merchant_customer_id: m["merchantCustomerId"],
first_name: m["firstName"],
last_name: m["lastName"],
email: m["email"],
phone: m["phone"],
locale: m["locale"],
cards: m["cards"] || [],
raw: m
}
end
end
# ── Bank Account Verification (also used by Interac Verification Service) ───
defmodule BankVerification do
@moduledoc """
Represents a Bank Account Validation or Interac Verification Service
verification session. Both products share this redirect-based shape.
"""
@type status :: :initiated | :completed | :failed | :cancelled | :expired
@type t :: %__MODULE__{
id: String.t() | nil,
merchant_ref_num: String.t() | nil,
session_id: String.t() | nil,
status: status(),
account_types: [String.t()] | [],
currency_codes: [String.t()] | [],
bank_scheme: String.t() | nil,
links: [map()] | [],
raw: map()
}
defstruct [
:id,
:merchant_ref_num,
:session_id,
:status,
:bank_scheme,
account_types: [],
currency_codes: [],
links: [],
raw: %{}
]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
merchant_ref_num: m["merchantRefNum"],
session_id: m["sessionId"],
status: parse_status(m["status"]),
bank_scheme: m["bankScheme"] || m["bankscheme"],
account_types: m["accountTypes"] || [],
currency_codes: m["currencyCodes"] || [],
links: m["links"] || [],
raw: m
}
end
@doc """
Convenience accessor: returns the redirect URL the customer should be
sent to, if present in `links`.
"""
@spec redirect_url(t()) :: String.t() | nil
def redirect_url(%__MODULE__{links: links}) do
Enum.find_value(links, fn
%{"rel" => "redirect_bank_validation", "href" => href} -> href
_ -> nil
end)
end
defp parse_status("INITIATED"), do: :initiated
defp parse_status("COMPLETED"), do: :completed
defp parse_status("FAILED"), do: :failed
defp parse_status("CANCELLED"), do: :cancelled
defp parse_status("EXPIRED"), do: :expired
defp parse_status(_), do: :initiated
end
# ── Customer Identity (KYC) ───────────────────────────────────────────────────
defmodule IdentityProfile do
@moduledoc "Represents a Customer Identity (KYC) check result."
@type decision :: :success | :error | :fail | :outsort | String.t()
@type t :: %__MODULE__{
id: String.t() | nil,
merchant_ref_num: String.t() | nil,
decision: decision() | nil,
provider_responses: map() | nil,
raw: map()
}
defstruct [:id, :merchant_ref_num, :decision, :provider_responses, raw: %{}]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
merchant_ref_num: m["merchantRefNum"],
decision: parse_decision(m["decision"]),
provider_responses: m["providerResponses"],
raw: m
}
end
# SUCCESS — passed the check.
# ERROR — could not be processed (downstream provider issue); rerun-able.
# FAIL — customer failed the check outright.
# OUTSORT — inconclusive; merchant should manually verify via documents.
defp parse_decision("SUCCESS"), do: :success
defp parse_decision("ERROR"), do: :error
defp parse_decision("FAIL"), do: :fail
defp parse_decision("OUTSORT"), do: :outsort
defp parse_decision(other), do: other
end
# ── Applications (Onboarding) ─────────────────────────────────────────────────
defmodule Application do
@moduledoc "Represents a merchant onboarding Application."
@type status :: :draft | :submitted | :approved | :declined | :in_review | String.t()
@type t :: %__MODULE__{
id: String.t() | nil,
status: status() | nil,
raw: map()
}
defstruct [:id, :status, raw: %{}]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"] || m["appId"],
status: parse_status(m["status"]),
raw: m
}
end
defp parse_status("DRAFT"), do: :draft
defp parse_status("SUBMITTED"), do: :submitted
defp parse_status("APPROVED"), do: :approved
defp parse_status("DECLINED"), do: :declined
defp parse_status("IN_REVIEW"), do: :in_review
defp parse_status(other), do: other
end
# ── Webhook Event ─────────────────────────────────────────────────────────────
defmodule WebhookEvent do
@moduledoc "Represents a decoded and verified Paysafe webhook event."
@type t :: %__MODULE__{
event_name: String.t() | nil,
event_type: String.t() | nil,
event_date: String.t() | nil,
type: String.t() | nil,
resource_id: String.t() | nil,
attempt_number: String.t() | nil,
payload: map(),
links: [map()] | [],
raw: map()
}
defstruct [
:event_name,
:event_type,
:event_date,
:type,
:resource_id,
:attempt_number,
payload: %{},
links: [],
raw: %{}
]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
event_name: m["eventName"],
event_type: m["eventType"],
event_date: m["eventDate"],
type: m["type"],
resource_id: m["resourceId"],
attempt_number: m["attemptNumber"],
payload: m["payload"] || %{},
links: m["links"] || [],
raw: m
}
end
end
# ── FX Rate ──────────────────────────────────────────────────────────────────
defmodule FxRate do
@moduledoc "Represents an FX rate quote from the FX Rates API."
@type t :: %__MODULE__{
id: String.t() | nil,
from_currency: String.t(),
to_currency: String.t(),
rate: float(),
expiry: String.t() | nil,
raw: map()
}
defstruct [:id, :from_currency, :to_currency, :rate, :expiry, raw: %{}]
@doc false
@spec from_map(map()) :: t()
def from_map(m) when is_map(m) do
%__MODULE__{
id: m["id"],
from_currency: m["fromCurrency"],
to_currency: m["toCurrency"],
rate: m["rate"],
expiry: m["expiry"],
raw: m
}
end
end
end