Packages
phoenix_kit
1.7.21
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps — SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/modules/billing/providers/providers.ex
defmodule PhoenixKit.Modules.Billing.Providers do
@moduledoc """
Provider registry and helper functions for payment providers.
This module serves as the central point for working with payment providers.
It handles provider lookup, availability checking, and configuration.
## Available Providers
- `:stripe` - Stripe payments (cards, wallets)
- `:paypal` - PayPal payments
- `:razorpay` - Razorpay payments (India)
## Usage
# Get a provider module
provider = Providers.get_provider(:stripe)
provider.create_checkout_session(invoice, opts)
# List available providers
Providers.list_available_providers()
#=> [:stripe, :paypal]
# Check if provider is available
Providers.provider_enabled?(:stripe)
#=> true
"""
alias PhoenixKit.Modules.Billing.Providers.Provider
alias PhoenixKit.Settings
@providers %{
stripe: PhoenixKit.Modules.Billing.Providers.Stripe,
paypal: PhoenixKit.Modules.Billing.Providers.PayPal,
razorpay: PhoenixKit.Modules.Billing.Providers.Razorpay
}
@provider_names Map.keys(@providers)
@doc """
Returns the provider module for the given provider name.
## Parameters
- `name` - Provider name as atom or string
## Returns
- Provider module if found
- `nil` if provider not found
## Examples
iex> Providers.get_provider(:stripe)
PhoenixKit.Modules.Billing.Providers.Stripe
iex> Providers.get_provider("paypal")
PhoenixKit.Modules.Billing.Providers.PayPal
iex> Providers.get_provider(:unknown)
nil
"""
@spec get_provider(atom() | String.t()) :: module() | nil
def get_provider(name) when is_atom(name), do: @providers[name]
def get_provider(name) when is_binary(name), do: @providers[String.to_existing_atom(name)]
@doc """
Returns a list of all provider names.
## Examples
iex> Providers.all_providers()
[:stripe, :paypal, :razorpay]
"""
@spec all_providers() :: [atom()]
def all_providers, do: @provider_names
@doc """
Returns a list of available (enabled and configured) provider names.
Checks each provider's `available?/0` callback to determine availability.
## Examples
iex> Providers.list_available_providers()
[:stripe, :paypal]
"""
@spec list_available_providers() :: [atom()]
def list_available_providers do
@providers
|> Enum.filter(fn {_name, module} ->
Code.ensure_loaded?(module) && function_exported?(module, :available?, 0) &&
module.available?()
end)
|> Enum.map(fn {name, _module} -> name end)
end
@doc """
Checks if a provider is enabled and available.
## Parameters
- `name` - Provider name as atom or string
## Returns
- `true` if provider is available
- `false` if provider is not available or not found
## Examples
iex> Providers.provider_enabled?(:stripe)
true
iex> Providers.provider_enabled?(:unknown)
false
"""
@spec provider_enabled?(atom() | String.t()) :: boolean()
def provider_enabled?(name) do
case get_provider(name) do
nil -> false
module -> Code.ensure_loaded?(module) && module.available?()
end
end
@doc """
Checks if a provider exists (regardless of availability).
## Examples
iex> Providers.provider_exists?(:stripe)
true
iex> Providers.provider_exists?(:bitcoin)
false
"""
@spec provider_exists?(atom() | String.t()) :: boolean()
def provider_exists?(name) when is_atom(name), do: Map.has_key?(@providers, name)
def provider_exists?(name) when is_binary(name) do
provider_exists?(String.to_existing_atom(name))
rescue
ArgumentError -> false
end
@doc """
Gets the setting key for a provider's enabled status.
## Examples
iex> Providers.enabled_setting_key(:stripe)
"billing_stripe_enabled"
"""
@spec enabled_setting_key(atom()) :: String.t()
def enabled_setting_key(provider) do
"billing_#{provider}_enabled"
end
@doc """
Checks if a provider is enabled in settings.
This is a lower-level check that only looks at the setting,
not whether the provider is fully configured.
## Examples
iex> Providers.setting_enabled?(:stripe)
true
"""
@spec setting_enabled?(atom()) :: boolean()
def setting_enabled?(provider) do
Settings.get_setting(enabled_setting_key(provider), "false") == "true"
end
@doc """
Creates a checkout session using the specified provider.
Convenience function that looks up the provider and calls
`create_checkout_session/2`.
## Parameters
- `provider` - Provider name
- `invoice` - Invoice to pay
- `opts` - Options passed to provider
## Returns
- `{:ok, checkout_session}` - Session created
- `{:error, :provider_not_found}` - Provider doesn't exist
- `{:error, :provider_not_available}` - Provider not configured
- `{:error, reason}` - Provider-specific error
"""
@spec create_checkout_session(atom() | String.t(), map(), keyword()) ::
{:ok, Provider.checkout_session()} | {:error, term()}
def create_checkout_session(provider, invoice, opts \\ []) do
with {:ok, module} <- get_available_provider(provider) do
module.create_checkout_session(invoice, opts)
end
end
@doc """
Creates a setup session using the specified provider.
## Parameters
- `provider` - Provider name
- `user` - User to save payment method for
- `opts` - Options passed to provider
## Returns
- `{:ok, setup_session}` - Session created
- `{:error, reason}` - Failed
"""
@spec create_setup_session(atom() | String.t(), map(), keyword()) ::
{:ok, Provider.setup_session()} | {:error, term()}
def create_setup_session(provider, user, opts \\ []) do
with {:ok, module} <- get_available_provider(provider) do
module.create_setup_session(user, opts)
end
end
@doc """
Charges a saved payment method using the appropriate provider.
## Parameters
- `payment_method` - Saved payment method record (must include :provider)
- `amount` - Amount to charge
- `opts` - Options passed to provider
## Returns
- `{:ok, charge_result}` - Charge successful
- `{:error, reason}` - Charge failed
"""
@spec charge_payment_method(map(), Decimal.t(), keyword()) ::
{:ok, Provider.charge_result()} | {:error, term()}
def charge_payment_method(%{provider: provider} = payment_method, amount, opts \\ []) do
with {:ok, module} <- get_available_provider(provider) do
module.charge_payment_method(payment_method, amount, opts)
end
end
@doc """
Verifies a webhook signature for the specified provider.
## Parameters
- `provider` - Provider name
- `payload` - Raw request body
- `signature` - Signature from headers
- `secret` - Webhook secret
## Returns
- `:ok` - Signature valid
- `{:error, :invalid_signature}` - Signature invalid
- `{:error, :provider_not_found}` - Provider doesn't exist
"""
@spec verify_webhook_signature(atom() | String.t(), binary(), String.t(), String.t()) ::
:ok | {:error, term()}
def verify_webhook_signature(provider, payload, signature, secret) do
case get_provider(provider) do
nil -> {:error, :provider_not_found}
module -> module.verify_webhook_signature(payload, signature, secret)
end
end
@doc """
Handles a webhook event for the specified provider.
## Parameters
- `provider` - Provider name
- `payload` - Decoded JSON payload
## Returns
- `{:ok, webhook_event}` - Event parsed
- `{:error, reason}` - Failed to parse
"""
@spec handle_webhook_event(atom() | String.t(), map()) ::
{:ok, Provider.webhook_event()} | {:error, term()}
def handle_webhook_event(provider, payload) do
case get_provider(provider) do
nil -> {:error, :provider_not_found}
module -> module.handle_webhook_event(payload)
end
end
@doc """
Creates a refund using the appropriate provider.
## Parameters
- `provider` - Provider name
- `provider_transaction_id` - Provider's transaction ID
- `amount` - Amount to refund (nil for full refund)
- `opts` - Options
## Returns
- `{:ok, refund_result}` - Refund created
- `{:error, reason}` - Refund failed
"""
@spec create_refund(atom() | String.t(), String.t(), Decimal.t() | nil, keyword()) ::
{:ok, Provider.refund_result()} | {:error, term()}
def create_refund(provider, provider_transaction_id, amount, opts \\ []) do
with {:ok, module} <- get_available_provider(provider) do
module.create_refund(provider_transaction_id, amount, opts)
end
end
@doc """
Returns display information for a provider.
## Examples
iex> Providers.provider_info(:stripe)
%{name: "Stripe", icon: "stripe", color: "#635BFF"}
"""
@spec provider_info(atom()) :: map()
def provider_info(:stripe) do
%{
name: "Stripe",
icon: "stripe",
color: "#635BFF",
description: "Accept cards, wallets, and more"
}
end
def provider_info(:paypal) do
%{
name: "PayPal",
icon: "paypal",
color: "#003087",
description: "PayPal and credit/debit cards"
}
end
def provider_info(:razorpay) do
%{
name: "Razorpay",
icon: "razorpay",
color: "#072654",
description: "Popular payment gateway in India"
}
end
def provider_info(_), do: %{name: "Unknown", icon: "credit-card", color: "#6B7280"}
# Private helpers
defp get_available_provider(provider) do
case get_provider(provider) do
nil ->
{:error, :provider_not_found}
module ->
if Code.ensure_loaded?(module) && function_exported?(module, :available?, 0) &&
module.available?() do
{:ok, module}
else
{:error, :provider_not_available}
end
end
end
end