Zepto

Elixir client for the Zepto Payments API.

Installation

Add zepto to your list of dependencies in mix.exs:

def deps do
  [
    {:zepto, "~> 0.1.0"}
  ]
end

Configuration

1. Add Zepto configuration

In your config/config.exs or environment-specific config:

config :zepto,
  environments: [
    sandbox: [
      base_url: "https://api.sandbox.zeptopayments.com",
      oauth_url: "https://go.sandbox.zeptopayments.com",
      client_id: System.get_env("ZEPTO_SANDBOX_CLIENT_ID"),
      client_secret: System.get_env("ZEPTO_SANDBOX_CLIENT_SECRET"),
      redirect_uri: "https://yourapp.com/zepto/sandbox/callback"
    ],
    prod: [
      base_url: "https://api.zeptopayments.com",
      oauth_url: "https://go.zeptopayments.com",
      client_id: System.get_env("ZEPTO_CLIENT_ID"),
      client_secret: System.get_env("ZEPTO_CLIENT_SECRET"),
      redirect_uri: "https://yourapp.com/zepto/callback"
    ]
  ]

2. Set up the OAuth callback routes

Zepto uses OAuth2 Authorization Code flow. Add the callback plug to your Phoenix router:

# In lib/my_app_web/router.ex

defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  # ... your other pipelines and routes ...

  scope "/zepto" do
    # Production callback
    forward "/callback", Zepto.Plug.OAuthCallback,
      environment: :prod,
      on_success: &MyAppWeb.ZeptoController.on_oauth_success/2,
      on_error: &MyAppWeb.ZeptoController.on_oauth_error/2

    # Sandbox callback
    forward "/sandbox/callback", Zepto.Plug.OAuthCallback,
      environment: :sandbox,
      on_success: &MyAppWeb.ZeptoController.on_oauth_success/2,
      on_error: &MyAppWeb.ZeptoController.on_oauth_error/2
  end
end

3. Create callback handlers

# In lib/my_app_web/controllers/zepto_controller.ex

defmodule MyAppWeb.ZeptoController do
  use MyAppWeb, :controller

  def on_oauth_success(conn, _tokens) do
    conn
    |> put_flash(:info, "Successfully connected to Zepto!")
    |> redirect(to: "/settings")
  end

  def on_oauth_error(conn, error) do
    conn
    |> put_flash(:error, "Failed to connect to Zepto: #{inspect(error)}")
    |> redirect(to: "/settings")
  end
end

4. Initiate the OAuth flow

Create a link or button that redirects users to Zepto's authorization page:

# Generate the authorization URL
def zepto_auth_url(environment \\ :sandbox) do
  config = Application.get_env(:zepto, :environments)[environment]
  client_id = config[:client_id]
  redirect_uri = URI.encode_www_form(config[:redirect_uri])
  scope = "public+agreements+bank_accounts+contacts+open_agreements+payments+payment_requests+refunds+transfers+transactions+offline_access+webhooks"

  oauth_url = config[:oauth_url]
  "#{oauth_url}/authorize?client_id=#{client_id}&redirect_uri=#{redirect_uri}&response_type=code&scope=#{scope}"
end

In your template:

<a href={zepto_auth_url(:sandbox)}>Connect to Zepto (Sandbox)</a>

Usage

Once authenticated, you can use the API:

# Create a client for the sandbox environment
client = Zepto.client(:sandbox)

# List contacts
{:ok, contacts} = Zepto.Contacts.list(client)

# Get a specific contact
{:ok, contact} = Zepto.Contacts.get(client, "contact_id")

# Create a payment
{:ok, payment} = Zepto.Payments.create(client, %{
  description: "Payment for invoice #123",
  matures_at: "2024-01-15T00:00:00Z",
  your_bank_account_id: "your_bank_account_id",
  payouts: [
    %{
      amount: 10000,
      description: "Invoice payment",
      recipient_contact_id: "contact_id"
    }
  ]
})

# List transactions
{:ok, transactions} = Zepto.Transactions.list(client)

API Resources

The following API resources are available:

Token Management

The library manages OAuth tokens automatically:

Checking Authorization Status

# Check if we have a refresh token
Zepto.Client.TokenManager.has_refresh_token?(Zepto.TokenManager.Sandbox)
# => true or false

# Get the current refresh token (for debugging)
{:ok, token} = Zepto.Client.TokenManager.get_refresh_token(Zepto.TokenManager.Sandbox)

Persisting Tokens

By default, tokens are stored in memory and will be lost on application restart. To persist tokens across restarts, implement the Zepto.TokenCallback behaviour:

defmodule MyApp.ZeptoTokenStore do
  @behaviour Zepto.TokenCallback

  alias MyApp.Repo
  alias MyApp.ZeptoToken

  @impl true
  def save_tokens(environment, tokens) do
    attrs = %{
      environment: to_string(environment),
      refresh_token: tokens.refresh_token,
      access_token: tokens[:access_token],
      expires_at: tokens[:expires_at]
    }

    %ZeptoToken{}
    |> ZeptoToken.changeset(attrs)
    |> Repo.insert(on_conflict: :replace_all, conflict_target: :environment)
    |> case do
      {:ok, _} -> :ok
      {:error, changeset} -> {:error, changeset}
    end
  end

  @impl true
  def load_tokens(environment) do
    case Repo.get_by(ZeptoToken, environment: to_string(environment)) do
      nil ->
        {:ok, nil}

      token ->
        {:ok, %{
          refresh_token: token.refresh_token,
          access_token: token.access_token,
          expires_at: token.expires_at
        }}
    end
  end
end

Then add it to your config:

config :zepto,
  environments: [
    sandbox: [
      # ... other config ...
      token_callback: MyApp.ZeptoTokenStore
    ]
  ]

The callbacks are called:

Note: Tokens are loaded lazily to avoid startup ordering issues with your Repo.

License

MIT