PhoenixKit.Modules.Billing.Providers.Provider behaviour (phoenix_kit v1.7.69)
Copy Markdown View SourceBehaviour for payment providers.
Defines a unified interface for all payment systems (Stripe, PayPal, Razorpay). Each provider implements this behaviour to handle payments, refunds, and webhooks.
Provider Architecture
PhoenixKit uses Internal Subscription Control - subscriptions are managed in our database, not by providers. Providers only handle:
- One-time payments (checkout sessions)
- Saving payment methods for recurring billing
- Charging saved payment methods
- Processing refunds
Hosted Checkout Flow
1. User clicks "Pay with Stripe" on invoice
2. Backend calls create_checkout_session/2
3. User redirected to provider's checkout page
4. Provider processes payment
5. Provider sends webhook
6. WebhookProcessor updates invoice statusImplementation Example
defmodule PhoenixKit.Modules.Billing.Providers.Stripe do
@behaviour PhoenixKit.Modules.Billing.Providers.Provider
@impl true
def provider_name, do: :stripe
@impl true
def available? do
config = get_config()
config && config.enabled && config.api_key
end
@impl true
def create_checkout_session(invoice, opts) do
# Implementation
end
# ... other callbacks
end
Summary
Callbacks
Checks if the provider is configured and available for use.
Charges a saved payment method.
Creates a checkout session for one-time payment.
Creates a refund for a transaction.
Creates a setup session to save a payment method without charging.
Detaches/removes a saved payment method from the provider.
Gets details of a saved payment method.
Handles and normalizes a webhook event payload.
Returns the provider name as an atom.
Verifies webhook signature to ensure request is from the provider.
Types
@type charge_result() :: PhoenixKit.Modules.Billing.Providers.Types.ChargeResult.t()
@type checkout_session() :: PhoenixKit.Modules.Billing.Providers.Types.CheckoutSession.t()
@type payment_method() :: PhoenixKit.Modules.Billing.Providers.Types.PaymentMethodInfo.t()
@type refund_result() :: PhoenixKit.Modules.Billing.Providers.Types.RefundResult.t()
@type setup_session() :: PhoenixKit.Modules.Billing.Providers.Types.SetupSession.t()
@type webhook_event() :: PhoenixKit.Modules.Billing.Providers.Types.WebhookEventData.t()
Callbacks
@callback available?() :: boolean()
Checks if the provider is configured and available for use.
Returns true if:
- Provider is enabled in settings
- API credentials are configured
- Provider passed verification (if applicable)
Examples
iex> Stripe.available?()
true
@callback charge_payment_method( payment_method :: map(), amount :: Decimal.t(), opts :: keyword() ) :: {:ok, charge_result()} | {:error, term()}
Charges a saved payment method.
Used for subscription renewals. The payment method was previously saved during checkout or setup session.
Parameters
payment_method- The saved payment method recordamount- Amount to charge (Decimal)opts- Options::currency- Currency code (default: from payment method):description- Description for the charge:invoice_uuid- Associated invoice UUID:metadata- Additional metadata
Returns
{:ok, charge_result}- Charge successful{:error, :card_declined}- Card was declined{:error, :payment_method_expired}- Payment method expired{:error, reason}- Other error
@callback create_checkout_session(invoice :: map(), opts :: keyword()) :: {:ok, checkout_session()} | {:error, term()}
Creates a checkout session for one-time payment.
This is used for paying invoices. The user is redirected to the provider's hosted checkout page where they enter payment details.
Parameters
invoice- The invoice to pay (must include amount, currency, line_items)opts- Options::success_url- URL to redirect after successful payment:cancel_url- URL to redirect if user cancels:save_payment_method- Whether to save card for future use (default: false)
Returns
{:ok, checkout_session}- Session created, redirect user tosession.url{:error, reason}- Failed to create session
@callback create_refund( provider_transaction_id :: String.t(), amount :: Decimal.t() | nil, opts :: keyword() ) :: {:ok, refund_result()} | {:error, term()}
Creates a refund for a transaction.
Parameters
provider_transaction_id- The provider's transaction/charge IDamount- Amount to refund (Decimal, nil for full refund)opts- Options::reason- Reason for refund:metadata- Additional metadata
Returns
{:ok, refund_result}- Refund created{:error, :already_refunded}- Transaction already refunded{:error, reason}- Refund failed
@callback create_setup_session(user :: map(), opts :: keyword()) :: {:ok, setup_session()} | {:error, term()}
Creates a setup session to save a payment method without charging.
Used when a user wants to add a payment method for future subscriptions without making an immediate payment.
Parameters
user- The user to save payment method foropts- Options::success_url- URL to redirect after success:cancel_url- URL to redirect if user cancels
Returns
{:ok, setup_session}- Session created{:error, reason}- Failed to create session
Detaches/removes a saved payment method from the provider.
Parameters
provider_payment_method_id- The provider's payment method ID
Returns
:ok- Payment method removed{:error, :not_found}- Payment method not found{:error, reason}- Failed to remove
@callback get_payment_method_details(provider_payment_method_id :: String.t()) :: {:ok, payment_method()} | {:error, term()}
Gets details of a saved payment method.
Parameters
provider_payment_method_id- The provider's payment method ID
Returns
{:ok, payment_method}- Payment method details{:error, :not_found}- Payment method not found{:error, reason}- Failed to get details
@callback handle_webhook_event(payload :: map()) :: {:ok, webhook_event()} | {:error, term()}
Handles and normalizes a webhook event payload.
Converts provider-specific event format to a normalized format that can be processed by WebhookProcessor.
Parameters
payload- Decoded JSON payload from webhook
Returns
{:ok, webhook_event}- Event parsed successfully{:error, :unknown_event}- Event type not recognized{:error, reason}- Failed to parse event
@callback provider_name() :: atom()
Returns the provider name as an atom.
Examples
iex> Stripe.provider_name()
:stripe
iex> PayPal.provider_name()
:paypal
@callback verify_webhook_signature( payload :: binary(), signature :: String.t(), secret :: String.t() ) :: :ok | {:error, :invalid_signature}
Verifies webhook signature to ensure request is from the provider.
Parameters
payload- Raw request body as binarysignature- Signature from request headerssecret- Webhook secret for this provider
Returns
:ok- Signature is valid{:error, :invalid_signature}- Signature verification failed