defmodule Paysafe do @moduledoc """ Production-grade Elixir client for the Paysafe API. ## Overview This library provides complete coverage of the Paysafe developer platform: - **Payments API** — Payment handles, payments, settlements, refunds, payouts, verifications, and customer/vault management. - **Payment Scheduler API** — Plans and subscriptions for recurring billing. - **Applications API** — Programmatic merchant onboarding. - **Value Added Services** — FX Rates, Customer Identity (KYC), Bank Account Validation, Network Tokenization, Account Updater. - **Webhooks** — HMAC-SHA256 verification and typed event parsing. ## Configuration Build a config struct from your credentials: config = Paysafe.Config.new!( username: "1001062690", password: "B-qa2-0-...", environment: :test, account_id: "1009688230" ) Or load from `config/config.exs`: # config/config.exs config :paysafe, username: System.get_env("PAYSAFE_USERNAME"), password: System.get_env("PAYSAFE_PASSWORD"), environment: :test, account_id: "1009688230" # runtime: config = Paysafe.Config.from_env!() ## Quick start — card payment config = Paysafe.Config.new!( username: "...", password: "...", environment: :test, account_id: "1009688230" ) # 1. Create a payment handle {:ok, handle} = Paysafe.create_payment_handle(config, %{ merchant_ref_num: "order-#{:rand.uniform(999_999)}", amount: 5000, currency_code: "USD", payment_type: "CARD", transaction_type: "PAYMENT", card: %{ card_num: "4111111111111111", card_expiry: %{month: 12, year: 2030}, cvv: "123", holder_name: "Jane Doe" }, billing_details: %{ street: "123 Main St", city: "New York", state: "NY", country: "US", zip: "10001" }, settle_with_auth: true }) # 2. Use the token in a payment {:ok, payment} = Paysafe.create_payment(config, %{ merchant_ref_num: "payment-001", amount: 5000, currency_code: "USD", settle_with_auth: true, payment_handle_token: handle.payment_handle_token }) ## Payment method coverage The Payments API supports 30+ payment methods: - **Cards** — Visa, Mastercard, Amex, Discover, Debit, Prepaid, Corporate. - **Digital Wallets** — Apple Pay, Google Pay, PayPal, Venmo, Skrill, Neteller. - **Bank Transfers** — iDEAL, EPS, BLIK, Interac e-Transfer, Mazooma, Pay by Bank. - **Direct Debit** — ACH (US), BACS (UK), EFT (CA), SEPA (EU). - **Cash** — PaysafeCash. - **Vouchers** — PaysafeCard, Openbucks, Multibanco. - **LatAm** — SafetyPay (Pix, Boleto, KHIPU, MACH), PagoEfectivo, Rapid Transfer. - **Crypto** — Pay with Crypto. ## Architecture - Config validated at construction via `NimbleOptions`. - All requests have exponential backoff retry on transient failures. - Token-bucket rate limiting per account via `ExRated`. - Telemetry events on every request (`:paysafe, :request, :start/:stop/:exception`). - HMAC-SHA256 webhook verification with constant-time comparison. - All public functions return `{:ok, result} | {:error, %Paysafe.Error{}}`. """ alias Paysafe.Payments.{ Customers, PaymentHandles, Payments, Payouts, Refunds, Settlements, Verifications } alias Paysafe.Scheduler.{Plans, Subscriptions} alias Paysafe.Webhooks # ── Payments API ───────────────────────────────────────────────────────────── @doc """ Create a payment handle (tokenize a payment instrument). See `Paysafe.Payments.PaymentHandles.create/3` for full parameter docs. """ defdelegate create_payment_handle(config, params, opts \\ []), to: PaymentHandles, as: :create @doc """ Retrieve a payment handle by ID. """ defdelegate get_payment_handle(config, handle_id, opts \\ []), to: PaymentHandles, as: :get @doc """ Create a payment using a `paymentHandleToken`. See `Paysafe.Payments.Payments.create/3` for full parameter docs. """ defdelegate create_payment(config, params, opts \\ []), to: Payments, as: :create @doc """ Retrieve a payment by ID. """ defdelegate get_payment(config, payment_id, opts \\ []), to: Payments, as: :get @doc """ List payments with optional filters. """ defdelegate list_payments(config, opts \\ []), to: Payments, as: :list @doc """ Cancel an authorized (pre-settlement) payment. """ defdelegate cancel_payment(config, payment_id, opts \\ []), to: Payments, as: :cancel @doc """ Create a settlement for an authorized payment. """ defdelegate create_settlement(config, payment_id, params, opts \\ []), to: Settlements, as: :create @doc """ Cancel a pending settlement. """ defdelegate cancel_settlement(config, settlement_id, opts \\ []), to: Settlements, as: :cancel @doc """ Issue a refund for a completed payment or settlement. Pass the settlement ID (or the payment ID, if `settle_with_auth` was `true` on the original payment). """ defdelegate create_refund(config, settlement_id, params, opts \\ []), to: Refunds, as: :create @doc """ Cancel a pending refund. """ defdelegate cancel_refund(config, refund_id, opts \\ []), to: Refunds, as: :cancel @doc """ Create a standalone credit (payout for non-iGaming merchants). """ defdelegate standalone_credit(config, params, opts \\ []), to: Payouts, as: :standalone_credit @doc """ Create an original credit (payout for iGaming merchants). """ defdelegate original_credit(config, params, opts \\ []), to: Payouts, as: :original_credit @doc """ Create a card verification (zero-value auth check). """ defdelegate create_verification(config, params, opts \\ []), to: Verifications, as: :create @doc """ Create a customer profile in the vault. """ defdelegate create_customer(config, params, opts \\ []), to: Customers, as: :create @doc """ Retrieve a customer profile. """ defdelegate get_customer(config, customer_id, opts \\ []), to: Customers, as: :get @doc """ Retrieve a customer profile by your own `merchantCustomerId`. """ defdelegate get_customer_by_merchant_customer_id(config, merchant_customer_id, opts \\ []), to: Customers, as: :get_by_merchant_customer_id @doc """ Update a customer profile. """ defdelegate update_customer(config, customer_id, params, opts \\ []), to: Customers, as: :update @doc """ Delete a customer profile. """ defdelegate delete_customer(config, customer_id, opts \\ []), to: Customers, as: :delete @doc """ Create a multi-use payment handle (saved instrument) for a customer. """ defdelegate create_customer_payment_handle(config, customer_id, params, opts \\ []), to: Customers, as: :create_payment_handle @doc """ List saved payment handles for a customer. """ defdelegate list_customer_payment_handles(config, customer_id, opts \\ []), to: Customers, as: :list_payment_handles @doc """ Create a single-use customer token (SUCT), tokenizing a customer's entire saved profile (cards, addresses, bank mandates) for 900 seconds. """ defdelegate create_single_use_customer_token(config, customer_id, opts \\ []), to: Customers, as: :create_single_use_customer_token # ── Scheduler API ───────────────────────────────────────────────────────────── @doc """ Create a recurring billing plan. See `Paysafe.Scheduler.Plans.create/3` for full parameter docs. """ defdelegate create_plan(config, params, opts \\ []), to: Plans, as: :create @doc """ Retrieve a billing plan by ID. """ defdelegate get_plan(config, plan_id, opts \\ []), to: Plans, as: :get @doc """ List all billing plans. """ defdelegate list_plans(config, opts \\ []), to: Plans, as: :list @doc """ Update a billing plan. """ defdelegate update_plan(config, plan_id, params, opts \\ []), to: Plans, as: :update @doc """ Delete (discontinue) a billing plan. """ defdelegate delete_plan(config, plan_id, opts \\ []), to: Plans, as: :delete @doc """ Create a subscription for a customer under a plan. See `Paysafe.Scheduler.Subscriptions.create/3` for full parameter docs. """ defdelegate create_subscription(config, params, opts \\ []), to: Subscriptions, as: :create @doc """ Retrieve a subscription by ID. """ defdelegate get_subscription(config, subscription_id, opts \\ []), to: Subscriptions, as: :get @doc """ List subscriptions. """ defdelegate list_subscriptions(config, opts \\ []), to: Subscriptions, as: :list @doc """ Update a subscription. """ defdelegate update_subscription(config, subscription_id, params, opts \\ []), to: Subscriptions, as: :update @doc """ Cancel a subscription. """ defdelegate cancel_subscription(config, subscription_id, opts \\ []), to: Subscriptions, as: :cancel @doc """ Suspend a subscription (billing paused; can be re-activated). """ defdelegate suspend_subscription(config, subscription_id, opts \\ []), to: Subscriptions, as: :suspend @doc """ Re-activate a suspended subscription. """ defdelegate reactivate_subscription(config, subscription_id, opts \\ []), to: Subscriptions, as: :reactivate # ── Webhooks ───────────────────────────────────────────────────────────────── @doc """ Verify the HMAC-SHA256 signature and parse a webhook payload. See `Paysafe.Webhooks.verify_and_parse/3` for full docs. """ defdelegate verify_webhook(raw_body, signature, hmac_key), to: Webhooks, as: :verify_and_parse @doc """ Parse a verified webhook body. """ defdelegate parse_webhook(raw_body), to: Webhooks, as: :parse # ── Convenience helpers ──────────────────────────────────────────────────── @doc """ Check if a `PaymentHandle` requires a customer redirect. Returns `{:redirect, url}` or `:proceed`. ## Example {:ok, handle} = Paysafe.create_payment_handle(config, params) case Paysafe.handle_action(handle) do {:redirect, url} -> redirect(conn, url) :proceed -> create_payment(config, payment_params) end """ @spec handle_action(Paysafe.Types.PaymentHandle.t()) :: {:redirect, String.t()} | :proceed def handle_action(%Paysafe.Types.PaymentHandle{action: :redirect, links: links}) do redirect_url = Enum.find_value(links, fn %{"rel" => "payment_redirect", "href" => href} -> href _ -> nil end) {:redirect, redirect_url} end def handle_action(%Paysafe.Types.PaymentHandle{action: :none}), do: :proceed def handle_action(%Paysafe.Types.PaymentHandle{}), do: :proceed @doc """ Returns the currently configured package version. """ @spec version() :: String.t() def version, do: Application.spec(:paysafe, :vsn) |> to_string() end