Skip to content

Commit

Permalink
Wrap paddle actions with DB transactions (#2558)
Browse files Browse the repository at this point in the history
* Wrap paddle actions with DB transactions

* Bow to credo

* Make the paddle transactions crash when applicable

* s/changeset/details
  • Loading branch information
aerosol authored Jan 3, 2023
1 parent 1785653 commit 44afbaa
Show file tree
Hide file tree
Showing 2 changed files with 105 additions and 90 deletions.
191 changes: 103 additions & 88 deletions lib/plausible/billing/billing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,93 +12,28 @@ defmodule Plausible.Billing do
user_id |> active_subscription_query() |> Repo.exists?()
end

defp active_subscription_query(user_id) do
from s in Subscription,
where: s.user_id == ^user_id and s.status == "active",
order_by: [desc: s.inserted_at],
limit: 1
end

def subscription_created(params) do
params =
if present?(params["passthrough"]) do
params
else
user = Repo.get_by(Plausible.Auth.User, email: params["email"])
Map.put(params, "passthrough", user && user.id)
end

changeset = Subscription.changeset(%Subscription{}, format_subscription(params))

Repo.insert(changeset) |> after_subscription_update
Repo.transaction(fn ->
handle_subscription_created(params)
end)
end

def subscription_updated(params) do
subscription = Repo.get_by!(Subscription, paddle_subscription_id: params["subscription_id"])
changeset = Subscription.changeset(subscription, format_subscription(params))

Repo.update(changeset) |> after_subscription_update
Repo.transaction(fn ->
handle_subscription_updated(params)
end)
end

defp after_subscription_update({:ok, subscription}) do
user =
Repo.get(Plausible.Auth.User, subscription.user_id)
|> Map.put(:subscription, subscription)

user
|> maybe_remove_grace_period
|> check_lock_status
|> maybe_adjust_api_key_limits
end

defp after_subscription_update(err), do: err

def subscription_cancelled(params) do
subscription =
Repo.get_by(Subscription, paddle_subscription_id: params["subscription_id"])
|> Repo.preload(:user)

if subscription do
changeset =
Subscription.changeset(subscription, %{
status: params["status"]
})

case Repo.update(changeset) do
{:ok, updated} ->
PlausibleWeb.Email.cancellation_email(subscription.user)
|> Plausible.Mailer.send()

{:ok, updated}

err ->
err
end
else
{:ok, nil}
end
Repo.transaction(fn ->
handle_subscription_cancelled(params)
end)
end

def subscription_payment_succeeded(params) do
subscription = Repo.get_by(Subscription, paddle_subscription_id: params["subscription_id"])

if subscription do
{:ok, api_subscription} = paddle_api().get_subscription(subscription.paddle_subscription_id)

amount =
:erlang.float_to_binary(api_subscription["next_payment"]["amount"] / 1, decimals: 2)

changeset =
Subscription.changeset(subscription, %{
next_bill_amount: amount,
next_bill_date: api_subscription["next_payment"]["date"],
last_bill_date: api_subscription["last_payment"]["date"]
})

Repo.update(changeset)
else
{:ok, nil}
end
Repo.transaction(fn ->
handle_subscription_payment_succeeded(params)
end)
end

def change_plan(user, new_plan_id) do
Expand Down Expand Up @@ -247,6 +182,71 @@ defmodule Plausible.Billing do
end
end

defp handle_subscription_created(params) do
params =
if present?(params["passthrough"]) do
params
else
user = Repo.get_by(Plausible.Auth.User, email: params["email"])
Map.put(params, "passthrough", user && user.id)
end

%Subscription{}
|> Subscription.changeset(format_subscription(params))
|> Repo.insert!()
|> after_subscription_update()
end

defp handle_subscription_updated(params) do
Subscription
|> Repo.get_by!(paddle_subscription_id: params["subscription_id"])
|> Subscription.changeset(format_subscription(params))
|> Repo.update!()
|> after_subscription_update()
end

defp handle_subscription_cancelled(params) do
subscription =
Subscription
|> Repo.get_by(paddle_subscription_id: params["subscription_id"])
|> Repo.preload(:user)

if subscription do
changeset =
Subscription.changeset(subscription, %{
status: params["status"]
})

updated = Repo.update!(changeset)

subscription
|> Map.fetch!(:user)
|> PlausibleWeb.Email.cancellation_email()
|> Plausible.Mailer.send()

updated
end
end

defp handle_subscription_payment_succeeded(params) do
subscription = Repo.get_by(Subscription, paddle_subscription_id: params["subscription_id"])

if subscription do
{:ok, api_subscription} = paddle_api().get_subscription(subscription.paddle_subscription_id)

amount =
:erlang.float_to_binary(api_subscription["next_payment"]["amount"] / 1, decimals: 2)

subscription
|> Subscription.changeset(%{
next_bill_amount: amount,
next_bill_date: api_subscription["next_payment"]["date"],
last_bill_date: api_subscription["last_payment"]["date"]
})
|> Repo.update!()
end
end

defp has_active_enterprise_subscription(user) do
Plausible.Repo.exists?(
from(s in Plausible.Billing.Subscription,
Expand Down Expand Up @@ -287,24 +287,22 @@ defmodule Plausible.Billing do
if new_allowance > allowance_required do
user
|> Plausible.Auth.GracePeriod.remove_changeset()
|> Repo.update()
|> Repo.update!()
else
{:ok, user}
user
end

_ ->
{:ok, user}
user
end
end

defp check_lock_status({:ok, user}) do
defp check_lock_status(user) do
Plausible.Billing.SiteLocker.check_sites_for(user)
{:ok, user}
user
end

defp check_lock_status(err), do: err

defp maybe_adjust_api_key_limits({:ok, user}) do
defp maybe_adjust_api_key_limits(user) do
plan =
Repo.get_by(Plausible.Billing.EnterprisePlan,
user_id: user.id,
Expand All @@ -317,10 +315,27 @@ defmodule Plausible.Billing do
Repo.update_all(api_keys, set: [hourly_request_limit: plan.hourly_api_request_limit])
end

{:ok, user}
user
end

defp maybe_adjust_api_key_limits(err), do: err

def paddle_api(), do: Application.fetch_env!(:plausible, :paddle_api)

defp active_subscription_query(user_id) do
from s in Subscription,
where: s.user_id == ^user_id and s.status == "active",
order_by: [desc: s.inserted_at],
limit: 1
end

defp after_subscription_update(subscription) do
user =
Plausible.Auth.User
|> Repo.get!(subscription.user_id)
|> Map.put(:subscription, subscription)

user
|> maybe_remove_grace_period()
|> check_lock_status()
|> maybe_adjust_api_key_limits()
end
end
4 changes: 2 additions & 2 deletions lib/plausible_web/controllers/api/paddle_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ defmodule PlausibleWeb.Api.PaddleController do
json(conn, "")
end

defp webhook_response({:error, changeset}, conn, _params) do
Logger.error("Error processing Paddle webhook: #{inspect(changeset)}")
defp webhook_response({:error, details}, conn, _params) do
Logger.error("Error processing Paddle webhook: #{inspect(details)}")

conn |> send_resp(400, "") |> halt
end
Expand Down

0 comments on commit 44afbaa

Please sign in to comment.