# Zepto
Elixir client for the [Zepto Payments API](https://docs.zeptopayments.com/).
## Installation
Add `zepto` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:zepto, "~> 0.1.0"}
]
end
```
## Configuration
### 1. Add Zepto configuration
In your `config/config.exs` or environment-specific config:
```elixir
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:
```elixir
# 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
```elixir
# 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:
```elixir
# 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}/authorise?client_id=#{client_id}&redirect_uri=#{redirect_uri}&response_type=code&scope=#{scope}"
end
```
In your template:
```heex
<a href={zepto_auth_url(:sandbox)}>Connect to Zepto (Sandbox)</a>
```
## Usage
Once authenticated, you can use the API:
```elixir
# 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:
- `Zepto.Agreements` - Payment agreements
- `Zepto.BankAccounts` - Bank account management
- `Zepto.Contacts` - Contact management
- `Zepto.Payments` - Payment processing
- `Zepto.PaymentRequests` - Payment request management
- `Zepto.Refunds` - Refund processing
- `Zepto.Transactions` - Transaction history
- `Zepto.Transfers` - Transfer management
- `Zepto.User` - Current user info
- `Zepto.Webhooks` - Webhook management
- `Zepto.Sandbox` - Sandbox testing utilities
## Token Management
The library manages OAuth tokens automatically:
- Access tokens are refreshed automatically when they expire (every 2 hours)
- Refresh tokens are stored in the TokenManager process state
- Each configured environment gets its own TokenManager process
### Checking Authorization Status
```elixir
# 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:
```elixir
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:
```elixir
config :zepto,
environments: [
sandbox: [
# ... other config ...
token_callback: MyApp.ZeptoTokenStore
]
]
```
The callbacks are called:
- `load_tokens/1` - Lazily on first API call (to restore tokens)
- `save_tokens/2` - After each token refresh and after OAuth authorization
Note: Tokens are loaded lazily to avoid startup ordering issues with your Repo.
## License
MIT