File size: 3,385 Bytes
8da2481 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | 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
|