berrypod/lib/simpleshop_theme/stripe/setup.ex

136 lines
3.9 KiB
Elixir
Raw Normal View History

defmodule SimpleshopTheme.Stripe.Setup do
@moduledoc """
Handles Stripe account setup: key verification, automatic webhook
endpoint creation, and teardown.
"""
alias SimpleshopTheme.Settings
alias SimpleshopTheme.Secrets
require Logger
@webhook_events ["checkout.session.completed", "checkout.session.expired"]
@doc """
Verifies a Stripe API key by making a lightweight Balance API call.
"""
def verify_api_key(api_key) do
case Stripe.Balance.retrieve(%{}, api_key: api_key) do
{:ok, _balance} -> :ok
{:error, %Stripe.Error{message: message}} -> {:error, message}
{:error, _} -> {:error, "Could not connect to Stripe"}
end
end
@doc """
Full setup flow: verify key, store it, create webhook endpoint if possible.
Returns:
- `{:ok, :webhook_created}` key valid, webhook auto-created (production)
- `{:ok, :localhost}` key valid, but URL is localhost so webhook skipped
- `{:error, message}` key invalid or setup failed
"""
def connect(api_key) do
with :ok <- verify_api_key(api_key) do
Settings.put_secret("stripe_api_key", api_key)
case maybe_create_webhook(api_key) do
{:ok, result} ->
Secrets.load_stripe_config()
{:ok, result}
{:error, reason} ->
# Key is valid and stored, but webhook creation failed.
# Still load the key so checkout works (webhooks can be set up manually).
Secrets.load_stripe_config()
{:error, reason}
end
end
end
@doc """
Removes Stripe configuration and deletes the webhook endpoint from Stripe.
"""
def disconnect do
delete_existing_webhook()
for key <- ["stripe_api_key", "stripe_signing_secret", "stripe_webhook_endpoint_id"] do
Settings.delete_setting(key)
end
Application.delete_env(:stripity_stripe, :api_key)
Application.delete_env(:stripity_stripe, :signing_secret)
:ok
end
@doc """
Saves a manually-provided webhook signing secret (for dev mode / Stripe CLI).
"""
def save_signing_secret(signing_secret) do
Settings.put_secret("stripe_signing_secret", signing_secret)
Secrets.load_stripe_config()
end
@doc """
Returns the webhook URL for this app.
"""
def webhook_url do
"#{SimpleshopThemeWeb.Endpoint.url()}/webhooks/stripe"
end
@doc """
Returns true if the app is running on localhost (Stripe can't reach it).
"""
def localhost? do
url = SimpleshopThemeWeb.Endpoint.url()
uri = URI.parse(url)
uri.host in ["localhost", "127.0.0.1", "0.0.0.0", "::1"]
end
defp maybe_create_webhook(api_key) do
if localhost?() do
{:ok, :localhost}
else
delete_existing_webhook()
create_webhook(api_key)
end
end
defp create_webhook(api_key) do
params = %{
url: webhook_url(),
enabled_events: @webhook_events
}
case Stripe.WebhookEndpoint.create(params, api_key: api_key) do
{:ok, endpoint} ->
Settings.put_secret("stripe_signing_secret", endpoint.secret)
Settings.put_setting("stripe_webhook_endpoint_id", endpoint.id, "string")
Logger.info("Stripe webhook endpoint created: #{endpoint.id}")
{:ok, :webhook_created}
{:error, %Stripe.Error{message: message}} ->
Logger.warning("Failed to create Stripe webhook: #{message}")
{:error, message}
{:error, _} ->
{:error, "Failed to create webhook endpoint"}
end
end
defp delete_existing_webhook do
endpoint_id = Settings.get_setting("stripe_webhook_endpoint_id")
api_key = Settings.get_secret("stripe_api_key")
if endpoint_id && api_key do
case Stripe.WebhookEndpoint.delete(endpoint_id, api_key: api_key) do
{:ok, _} ->
Logger.info("Deleted Stripe webhook endpoint: #{endpoint_id}")
{:error, reason} ->
Logger.warning("Failed to delete webhook endpoint #{endpoint_id}: #{inspect(reason)}")
end
end
end
end