119 lines
3.3 KiB
Elixir
119 lines
3.3 KiB
Elixir
defmodule PlausibleWeb.Api.PaddleController do
|
|
use PlausibleWeb, :controller
|
|
use Plausible.Repo
|
|
require Logger
|
|
|
|
plug :verify_signature when action in [:webhook]
|
|
|
|
def webhook(conn, %{"alert_name" => "subscription_created"} = params) do
|
|
Plausible.Billing.subscription_created(params)
|
|
|> webhook_response(conn, params)
|
|
end
|
|
|
|
def webhook(conn, %{"alert_name" => "subscription_updated"} = params) do
|
|
Plausible.Billing.subscription_updated(params)
|
|
|> webhook_response(conn, params)
|
|
end
|
|
|
|
def webhook(conn, %{"alert_name" => "subscription_cancelled"} = params) do
|
|
Plausible.Billing.subscription_cancelled(params)
|
|
|> webhook_response(conn, params)
|
|
end
|
|
|
|
def webhook(conn, %{"alert_name" => "subscription_payment_succeeded"} = params) do
|
|
Plausible.Billing.subscription_payment_succeeded(params)
|
|
|> webhook_response(conn, params)
|
|
end
|
|
|
|
def webhook(conn, _params) do
|
|
send_resp(conn, 404, "") |> halt
|
|
end
|
|
|
|
@default_currency_fallback :EUR
|
|
|
|
def currency(conn, _params) do
|
|
plan_id = get_currency_reference_plan_id()
|
|
customer_ip = PlausibleWeb.RemoteIP.get(conn)
|
|
|
|
result =
|
|
Plausible.Cache.Adapter.fetch(:customer_currency, {plan_id, customer_ip}, fn ->
|
|
case Plausible.Billing.PaddleApi.fetch_prices([plan_id], customer_ip) do
|
|
{:ok, %{^plan_id => money}} ->
|
|
{:ok, money.currency}
|
|
|
|
error ->
|
|
Sentry.capture_message("Failed to fetch currency reference plan",
|
|
extra: %{error: inspect(error)}
|
|
)
|
|
|
|
{:error, :fetch_prices_failed}
|
|
end
|
|
end)
|
|
|
|
case result do
|
|
{:ok, currency} ->
|
|
conn
|
|
|> put_status(200)
|
|
|> json(%{currency: Cldr.Currency.currency_for_code!(currency).narrow_symbol})
|
|
|
|
{:error, :fetch_prices_failed} ->
|
|
conn
|
|
|> put_status(200)
|
|
|> json(%{
|
|
currency: Cldr.Currency.currency_for_code!(@default_currency_fallback).narrow_symbol
|
|
})
|
|
end
|
|
end
|
|
|
|
def verify_signature(conn, _opts) do
|
|
signature = Base.decode64!(conn.params["p_signature"])
|
|
|
|
msg =
|
|
Map.delete(conn.params, "p_signature")
|
|
|> Enum.map(fn {key, val} -> {key, "#{val}"} end)
|
|
|> List.keysort(0)
|
|
|> PhpSerializer.serialize()
|
|
|
|
[key_entry] = :public_key.pem_decode(get_paddle_key())
|
|
|
|
public_key = :public_key.pem_entry_decode(key_entry)
|
|
|
|
if :public_key.verify(msg, :sha, signature, public_key) do
|
|
conn
|
|
else
|
|
send_resp(conn, 400, "") |> halt
|
|
end
|
|
end
|
|
|
|
@paddle_currency_reference_plan_id "857097"
|
|
@paddle_sandbox_currency_reference_plan_id "63842"
|
|
defp get_currency_reference_plan_id() do
|
|
if Application.get_env(:plausible, :environment) == "staging" do
|
|
@paddle_sandbox_currency_reference_plan_id
|
|
else
|
|
@paddle_currency_reference_plan_id
|
|
end
|
|
end
|
|
|
|
@paddle_prod_key File.read!("priv/paddle.pem")
|
|
@paddle_sandbox_key File.read!("priv/paddle_sandbox.pem")
|
|
|
|
defp get_paddle_key() do
|
|
if Application.get_env(:plausible, :environment) == "staging" do
|
|
@paddle_sandbox_key
|
|
else
|
|
@paddle_prod_key
|
|
end
|
|
end
|
|
|
|
defp webhook_response({:ok, _}, conn, _params) do
|
|
json(conn, "")
|
|
end
|
|
|
|
defp webhook_response({:error, details}, conn, _params) do
|
|
Logger.error("Error processing Paddle webhook: #{inspect(details)}")
|
|
|
|
conn |> send_resp(400, "") |> halt
|
|
end
|
|
end
|