Current section

Files

Jump to
ept_sdk lib ept_sdk entity.ex
Raw

lib/ept_sdk/entity.ex

defmodule EPTSDK.Entity do
@moduledoc """
An individual record from the Edge API.
"""
def to_struct(
%{
"id" => id,
"type" => "payment_methods" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.PaymentMethod{
id: id,
type: type,
card_pan_token: fetch(attributes, "card_pan_token"),
card_cvv_token: fetch(attributes, "card_cvv_token"),
expiry_month: fetch(attributes, "expiry_month"),
expiry_year: fetch(attributes, "expiry_year"),
card_bin: fetch(attributes, "card_bin"),
card_last_four: fetch(attributes, "card_last_four"),
external_state: fetch(attributes, "external_state"),
kind: fetch(attributes, "kind"),
discarded_at: fetch_datetime(attributes, "discarded_at"),
created_at: fetch_datetime(attributes, "created_at"),
updated_at: fetch_datetime(attributes, "updated_at"),
merchant:
fetch_relationship(record["relationships"], "merchant", included),
payment_demands: fetch_relationship(record["relationships"], "payment_demands", included),
customer: fetch_relationship(record["relationships"], "customer", included),
address: fetch_relationship(record["relationships"], "address", included),
__relationships__: record["relationships"],
__links__: record["links"] || links,
__raw__: record
}
def to_struct(
%{
"id" => id,
"type" => "payment_demands" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.PaymentDemand{
id: id,
type: type,
amount: fetch_money(attributes, ["amount_cents", "currency"]),
amount_cents: fetch(attributes, "amount_cents"),
fee: fetch_money(attributes, ["fee_cents", "currency"]),
fee_cents: fetch(attributes, "fee_cents"),
gateway: fetch_money(attributes, ["gateway_cents", "currency"]),
gateway_cents: fetch(attributes, "gateway_cents"),
refund: fetch_money(attributes, ["refund_cents", "currency"]),
refund_cents: fetch(attributes, "refund_cents"),
net: fetch_money(attributes, ["net_cents", "currency"]),
net_cents: fetch(attributes, "net_cents"),
currency: fetch(attributes, "currency"),
description: fetch(attributes, "description"),
created_at: fetch_datetime(attributes, "created_at"),
updated_at: fetch_datetime(attributes, "updated_at"),
customer: fetch_relationship(record["relationships"], "customer", included),
payment_method: fetch_relationship(record["relationships"], "payment_method", included),
billing_address: fetch_relationship(record["relationships"], "billing_address", included),
payment_subscription: fetch_relationship(record["relationships"], "payment_subscription", included),
shipping_address:
fetch_relationship(record["relationships"], "shipping_address", included),
merchant:
fetch_relationship(record["relationships"], "merchant", included),
__relationships__: record["relationships"],
__links__: record["links"] || links,
__raw__: record
}
def to_struct(
%{
"id" => id,
"type" => "consumer_addresses" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.ConsumerAddress{
id: id,
type: type,
line_1: fetch(attributes, "line_1"),
line_2: fetch(attributes, "line_2"),
city: fetch(attributes, "city"),
state: fetch(attributes, "state"),
zip: fetch(attributes, "zip"),
country: fetch(attributes, "country"),
created_at: fetch_datetime(attributes, "created_at"),
updated_at: fetch_datetime(attributes, "updated_at"),
merchant:
fetch_relationship(record["relationships"], "merchant", included),
__relationships__: record["relationships"],
__links__: record["links"] || links,
__raw__: record
}
def to_struct(
%{
"id" => id,
"type" => "customers" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.Customer{
id: id,
type: type,
name: fetch(attributes, "name"),
email: fetch(attributes, "email"),
phone_number: fetch(attributes, "phone_number"),
discarded_at: fetch(attributes, "discarded_at"),
blocked_at: fetch_datetime(attributes, "blocked_at"),
created_at: fetch_datetime(attributes, "created_at"),
updated_at: fetch_datetime(attributes, "updated_at"),
address: fetch_relationship(record["relationships"], "address", included),
payment_methods: fetch_relationship(record["relationships"], "payment_methods", included),
merchant:
fetch_relationship(record["relationships"], "merchant", included),
payment_demands: fetch_relationship(record["relationships"], "payment_demands", included),
# TODO: turn into formal relationship structs
__relationships__: record["relationships"],
# TODO: turn into formal links structs
__links__: record["links"] || links,
__raw__: record
}
def to_struct(
%{
"id" => id,
"type" => "merchants" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.Merchant{
id: id,
type: type,
average_monthly_transaction_volume_cents:
fetch(attributes, "average_monthly_transaction_volume_cents"),
average_transaction_size_cents: fetch(attributes, "average_transaction_size_cents"),
business_address: fetch(attributes, "business_address"),
business_address_line_2: fetch(attributes, "business_address_line_2"),
business_city_name: fetch(attributes, "business_city_name"),
business_country: fetch(attributes, "business_country"),
business_description: fetch(attributes, "business_description"),
business_name: fetch(attributes, "business_name"),
business_privacy_url: fetch(attributes, "business_privacy_url"),
business_state: fetch(attributes, "business_state"),
business_support_email: fetch(attributes, "business_support_email"),
business_support_url: fetch(attributes, "business_support_url"),
business_terms_url: fetch(attributes, "business_terms_url"),
business_timezone: fetch(attributes, "business_timezone"),
business_website: fetch(attributes, "business_website"),
business_zip_code: fetch(attributes, "business_zip_code"),
max_transaction_size_cents: fetch(attributes, "max_transaction_size_cents"),
phone_number: fetch(attributes, "phone_number"),
public_business_name: fetch(attributes, "public_business_name"),
shortened_descriptor: fetch(attributes, "shortened_descriptor"),
statement_descriptor: fetch(attributes, "statement_descriptor"),
active_at: fetch_datetime(attributes, "active_at"),
manual_review_at: fetch_datetime(attributes, "manual_review_at"),
pending_at: fetch_datetime(attributes, "pending_at"),
created_at: fetch_datetime(attributes, "created_at"),
updated_at: fetch_datetime(attributes, "updated_at"),
customers: fetch_relationship(record["relationships"], "customers", included),
consumer_addresses:
fetch_relationship(record["relationships"], "consumer_addresses", included),
payment_methods: fetch_relationship(record["relationships"], "payment_methods", included),
payment_demands: fetch_relationship(record["relationships"], "payment_demands", included),
events: fetch_relationship(record["relationships"], "events", included),
webhook_subscriptions:
fetch_relationship(record["relationships"], "webhook_subscriptions", included),
payment_subscriptions: fetch_relationship(record["relationships"], "payment_subscriptions", included),
# TODO: turn into formal relationship structs
__relationships__: record["relationships"],
# TODO: turn into formal links structs
__links__: record["links"] || links,
__raw__: record
}
def to_struct(
%{
"id" => id,
"type" => "events" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.Event{
id: id,
type: type,
code: fetch(attributes, "code"),
payload: fetch(attributes, "payload"),
occurred_at: fetch_datetime(attributes, "occurred_at"),
created_at: fetch_datetime(attributes, "created_at"),
merchant:
fetch_relationship(record["relationships"], "merchant", included),
# TODO: turn into formal relationship structs
__relationships__: record["relationships"],
# TODO: turn into formal links structs
__links__: record["links"] || links,
__raw__: record
}
def to_struct(
%{
"id" => id,
"type" => "payment_subscriptions" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.PaymentSubscriptions{
id: id,
type: type,
amount_cents: fetch(attributes, "amount_cents"),
amount_currency: fetch(attributes, "amount_currency"),
billing_period: fetch(attributes, "billing_period"),
description: fetch(attributes, "description"),
next_billing_day: fetch(attributes, "next_billing_day"),
status: fetch(attributes, "status"),
next_billing: fetch_datetime(attributes, "next_billing"),
discarded_at: fetch_datetime(attributes, "discarded_at"),
end_at: fetch_datetime(attributes, "end_at"),
created_at: fetch_datetime(attributes, "created_at"),
updated_at: fetch_datetime(attributes, "updated_at"),
merchant:
fetch_relationship(record["relationships"], "merchant", included),
payment_demands: fetch_relationship(record["relationships"], "payment_demands", included),
payment_method: fetch_relationship(record["relationships"], "payment_method", included),
customer: fetch_relationship(record["relationships"], "customer", included),
# TODO: turn into formal relationship structs
__relationships__: record["relationships"],
# TODO: turn into formal links structs
__links__: record["links"] || links,
__raw__: record
}
def to_struct(
%{
"id" => id,
"type" => "webhook_subscriptions" = type,
"attributes" => attributes
} = record,
links,
included
),
do: %EPTSDK.WebhookSubscription{
id: id,
type: type,
active: fetch(attributes, "active"),
concurrency_limit: fetch(attributes, "concurrency_limit"),
description: fetch(attributes, "description"),
events: fetch(attributes, "events"),
secret_key: fetch(attributes, "secret_key"),
url: fetch(attributes, "url"),
created_at: fetch_datetime(attributes, "created_at"),
updated_at: fetch_datetime(attributes, "updated_at"),
merchant:
fetch_relationship(record["relationships"], "merchant", included),
# TODO: turn into formal relationship structs
__relationships__: record["relationships"],
# TODO: turn into formal links structs
__links__: record["links"] || links,
__raw__: record
}
def to_struct(record, links, included) when not is_map_key(record, "attributes") do
to_struct(Map.put(record, "attributes", %{}), links, included)
end
defp fetch(attributes, key) when is_map(attributes) and is_binary(key) do
if Map.has_key?(attributes, key) do
attributes[key]
else
%EPTSDK.PropertyNotAvailable{name: key, reason: :unfetched}
end
end
defp fetch_money(attributes, [cents_key, currency_key])
when is_map(attributes) and is_binary(cents_key) and is_binary(currency_key) do
with true <- Map.has_key?(attributes, cents_key),
true <- Map.has_key?(attributes, currency_key),
cents when is_integer(cents) <- Map.get(attributes, cents_key),
currency when is_binary(currency) <- Map.get(attributes, currency_key) do
Money.new(cents, currency)
else
false -> %EPTSDK.PropertyNotAvailable{name: [cents_key, currency_key], reason: :unfetched}
nil -> nil
_ -> %EPTSDK.PropertyNotAvailable{name: [cents_key, currency_key], reason: :decoding_error}
end
end
defp fetch_datetime(attributes, key) when is_map(attributes) and is_binary(key) do
with true <- Map.has_key?(attributes, key),
value when not is_nil(value) <- Map.get(attributes, key),
{:ok, timestamp} <- Timex.parse(value, "{ISO:Extended:Z}") do
timestamp
else
false -> %EPTSDK.PropertyNotAvailable{name: key, reason: :unfetched}
nil -> nil
{:error, _message} -> %EPTSDK.PropertyNotAvailable{name: key, reason: :decoding_error}
end
end
defp fetch_relationship(relationships, key, []) do
Map.get(relationships, key)
|> case do
nil ->
%EPTSDK.RelationshipNotAvailable{
name: key,
reason: :undefined_relationship
}
_relationship ->
%EPTSDK.RelationshipNotAvailable{name: key, reason: :not_included}
end
end
defp fetch_relationship(relationships, key, included)
when is_map(relationships) and is_binary(key) and is_list(included) do
Map.get(relationships, key)
|> case do
nil -> {:error, :unknown_relationship}
relationship -> {:ok, decode_relationship(relationship)}
end
|> case do
{:error, _} = error -> error
{:ok, nil} -> {:ok, []}
{:ok, relation} -> {:ok, find_by_relation(included, relation)}
end
|> case do
{:error, _} = error -> error
{:ok, nil} -> {:error, :not_included}
{:ok, data} when is_list(data) -> data |> Enum.map(&to_struct(&1, nil, included))
{:ok, data} -> to_struct(data, nil, included)
end
|> case do
{:error, reason} ->
%EPTSDK.RelationshipNotAvailable{
name: key,
reason: reason
}
record ->
record
end
end
defp decode_relationship(%{"data" => %{"id" => id, "type" => type}} = relationship)
when is_map(relationship) do
{id, type}
end
defp decode_relationship(%{} = relationship) when is_map(relationship), do: nil
defp find_by_relation(included, {id, type}),
do:
Enum.find(included, fn %{"id" => included_id, "type" => included_type} ->
included_type == type && included_id == id
end)
end