| defmodule Plausible.Billing.DevSubscriptions do |
| @moduledoc """ |
| Module for conveniently handling subscriptions in the :dev environment. |
| """ |
|
|
| use Plausible |
|
|
| on_ee do |
| import Ecto.Query |
|
|
| alias Plausible.{Repo, Billing} |
| alias Plausible.Billing.{Plan, EnterprisePlan, DevPaddleApiMock} |
| alias PlausibleWeb.Router.Helpers, as: Routes |
|
|
| def create_after_1s(team_id, plan_id) do |
| Task.start(fn -> |
| Process.sleep(1000) |
| create(team_id, plan_id) |
| end) |
| end |
|
|
| def create(team_id, plan_id, opts \\ []) do |
| plan_or_enterprise_plan = get_plan_or_enterprise_plan(plan_id) |
|
|
| if plan_or_enterprise_plan do |
| if Keyword.get(opts, :clean_existing, true) do |
| delete(team_id) |
| end |
|
|
| %{ |
| "event_time" => to_string(NaiveDateTime.utc_now(:second)), |
| "alert_name" => "subscription_created", |
| "passthrough" => "ee:true;user:0;team:#{team_id}", |
| "email" => "", |
| "subscription_id" => Ecto.UUID.generate(), |
| "subscription_plan_id" => plan_id, |
| "update_url" => Routes.dev_subscription_path(PlausibleWeb.Endpoint, :update_form), |
| "cancel_url" => Routes.dev_subscription_path(PlausibleWeb.Endpoint, :cancel_form), |
| "status" => "active", |
| "next_bill_date" => next_bill_date(plan_or_enterprise_plan, plan_id), |
| "unit_price" => "#{to_string(DevPaddleApiMock.all_prices()[plan_id])}.00", |
| "currency" => "EUR" |
| } |
| |> Plausible.Billing.subscription_created() |
|
|
| {:ok, Repo.reload(get_team_subscription(team_id))} |
| else |
| {:error, "Plan with id '#{plan_id}' not found."} |
| end |
| end |
|
|
| def update(team_id, new_status) do |
| if subscription = get_team_subscription(team_id) do |
| current_plan_id = subscription.paddle_plan_id |
| current_plan = get_plan_or_enterprise_plan(current_plan_id) |
|
|
| next_bill_date = |
| case new_status do |
| "active" -> next_bill_date(current_plan, current_plan_id) |
| "past_due" -> Date.utc_today() |> Date.shift(day: 5) |> Date.to_iso8601() |
| "paused" -> subscription.next_bill_date |> Date.to_iso8601() |
| end |
|
|
| %{ |
| "subscription_id" => subscription.paddle_subscription_id, |
| "subscription_plan_id" => current_plan_id, |
| "update_url" => subscription.update_url, |
| "cancel_url" => subscription.cancel_url, |
| "old_status" => to_string(subscription.status), |
| "status" => new_status, |
| "next_bill_date" => next_bill_date, |
| "new_unit_price" => "#{to_string(DevPaddleApiMock.all_prices()[current_plan_id])}.00", |
| "currency" => "EUR" |
| } |
| |> Plausible.Billing.subscription_updated() |
|
|
| :ok |
| else |
| {:error, :no_subscription} |
| end |
| end |
|
|
| def delete(team_id, opts \\ []) do |
| delete_enterprise? = Keyword.get(opts, :delete_enterprise?, false) |
|
|
| if subscription = get_team_subscription(team_id) do |
| Repo.delete(subscription) |
|
|
| if delete_enterprise? do |
| Plausible.Repo.delete_all(from(e in EnterprisePlan, where: e.team_id == ^team_id)) |
| end |
|
|
| :ok |
| else |
| {:error, :no_subscription} |
| end |
| end |
|
|
| def cancel(team_id, opts \\ []) do |
| set_expired? = Keyword.get(opts, :set_expired?, false) |
|
|
| if subscription = get_team_subscription(team_id) do |
| %{"subscription_id" => subscription.paddle_subscription_id, "status" => "deleted"} |
| |> Billing.subscription_cancelled() |
|
|
| subscription = Repo.reload!(subscription) |
|
|
| if set_expired? do |
| subscription |
| |> Ecto.Changeset.change(%{next_bill_date: Date.utc_today() |> Date.add(-1)}) |
| |> Repo.update() |
| else |
| {:ok, subscription} |
| end |
| else |
| {:error, :no_subscription} |
| end |
| end |
|
|
| defp get_plan_or_enterprise_plan(plan_id) do |
| Billing.Plans.find(plan_id) || Repo.get_by(EnterprisePlan, paddle_plan_id: plan_id) |
| end |
|
|
| defp get_team_subscription(team_id) do |
| Plausible.Teams.Team |
| |> Plausible.Repo.get(team_id) |
| |> Plausible.Teams.with_subscription() |
| |> Map.get(:subscription) |
| end |
|
|
| defp next_bill_date(%EnterprisePlan{} = plan, _) do |
| if plan.billing_interval == :monthly, do: next_month(), else: next_year() |
| end |
|
|
| defp next_bill_date(%Plan{} = plan, plan_id) do |
| if plan_id == plan.monthly_product_id, do: next_month(), else: next_year() |
| end |
|
|
| defp next_month(), do: Date.utc_today() |> Date.shift(month: 1) |> Date.to_iso8601() |
| defp next_year(), do: Date.utc_today() |> Date.shift(year: 1) |> Date.to_iso8601() |
| end |
| end |
|
|