README.md

# Aplyid

An Elixir client library for the [APLYiD Developer API](https://docs.aplyid.com/).

## Installation

Add `aplyid` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:aplyid, "~> 0.1.0"}
  ]
end
```

## Configuration

Configure environments in your `config/runtime.exs`:

```elixir
config :aplyid,
  environments: [
    uat: [
      base_url: "https://integration.aplyid.com",
      api_key: System.get_env("APLYID_UAT_API_KEY"),
      api_secret: System.get_env("APLYID_UAT_API_SECRET")
    ],
    production: [
      base_url: "https://app.aplyid.com",
      api_key: System.get_env("APLYID_API_KEY"),
      api_secret: System.get_env("APLYID_API_SECRET")
    ]
  ]
```

## Usage

### Creating a Client

```elixir
# Create a client for an environment
client = Aplyid.Client.new(environment: :uat)

# Shorthand for UAT
client = Aplyid.Client.uat()
```

### Identity Verification

```elixir
# Create a verification (sends SMS to user)
{:ok, result} = Aplyid.Client.Verifications.create(client, %{
  "reference" => "order-123",
  "email" => "user@example.com",
  "contact_phone" => "61400000000",
  "firstname" => "John",
  "lastname" => "Smith",
  "flow_type" => "SIMPLE2"
})

# result contains:
# %{"transaction_id" => "eUmE4voe-L_BRzjG"}

# When no contact_phone is provided, the response includes a URL for manual verification:
# %{"transaction_id" => "...", "start_process_url" => "https://..."}
```

```elixir
# Resend SMS notification
{:ok, result} = Aplyid.Client.Verifications.resend_sms(client, "transaction_id")

# Resend to a different phone number
{:ok, result} = Aplyid.Client.Verifications.resend_sms(client, "transaction_id", %{
  "contact_phone" => "61400111222"
})
```

### Webhook Notifications

Mount the webhook plug in your Phoenix router to receive verification events:

```elixir
# lib/my_app_web/router.ex
forward "/webhooks/aplyid/uat", Aplyid.Webhook.Plug,
  environment: :uat,
  handler: MyApp.AplyidWebhookHandler
```

Implement the handler with `use Aplyid.Webhook.Handler`:

```elixir
defmodule MyApp.AplyidWebhookHandler do
  use Aplyid.Webhook.Handler

  @impl true
  def handle_event("completed", payload, _context) do
    # Verification completed - payload contains results
    :ok
  end

  def handle_event(_event_type, _payload, _context), do: :ok
end
```

Event types: `"created"`, `"completed"`, `"updated"`, `"archived"`, `"error"`, `"pending"`, `"test"`.

#### Webhook Authentication

Authorization is verified automatically before `handle_event/3` is called. Configure `webhook_auth` in your environment config to match the token APLYiD sends:

```elixir
config :aplyid,
  environments: [
    uat: [
      base_url: "https://integration.aplyid.com",
      api_key: System.get_env("APLYID_UAT_API_KEY"),
      api_secret: System.get_env("APLYID_UAT_API_SECRET"),
      webhook_auth: System.get_env("APLYID_UAT_WEBHOOK_AUTH")
    ]
  ]
```

If `webhook_auth` is not configured, all requests are allowed. If configured, the plug compares the incoming `Authorization` header and returns 401 on mismatch.

To use custom auth logic, override `verify_authorization/1` in your handler:

```elixir
defmodule MyApp.AplyidWebhookHandler do
  use Aplyid.Webhook.Handler

  @impl true
  def verify_authorization(%{authorization: auth}) do
    if MyApp.Auth.valid_webhook_token?(auth), do: :ok, else: {:error, :unauthorized}
  end

  @impl true
  def handle_event("completed", payload, _context) do
    # ...
    :ok
  end
end
```

### Error Handling

All API calls return `{:ok, data}` or `{:error, error}`:

```elixir
case Aplyid.Client.Verifications.create(client, params) do
  {:ok, result} ->
    result["transaction_id"]

  {:error, %Aplyid.Client.Error{type: :unauthorized}} ->
    # Invalid API credentials

  {:error, %Aplyid.Client.Error{type: :validation_error} = error} ->
    # Validation failed - check error.details

  {:error, error} ->
    Aplyid.Client.Error.message(error)
end
```

## Mock Server

The library includes a built-in mock server for local development and testing. This allows you to:

- Develop without API credentials
- Run integration tests without hitting the real API
- Avoid per-transaction billing during development
- Walk through a mock verification UI flow

### Setting Up the Mock Server

#### 1. Add Optional Dependencies

The mock server requires Plug and optionally Ecto for persistence:

```elixir
def deps do
  [
    {:aplyid, "~> 0.1.0"},
    {:plug, "~> 1.14"},
    {:ecto_sql, "~> 3.10"},
    {:postgrex, "~> 0.17"}
  ]
end
```

#### 2. Configure the Mock Server

```elixir
# config/dev.exs or config/test.exs
config :aplyid, :mock_server,
  enabled: true,
  embedded: true,
  base_url: "http://localhost:4000/aplyid-mock",
  repo: MyApp.Repo
```

Configuration options:

| Option | Description | Default |
|--------|-------------|---------|
| `enabled` | Enable the mock server | `false` |
| `embedded` | Skip starting Bandit (use Phoenix instead) | `false` |
| `base_url` | Base URL for verification links | `"http://localhost:4000"` |
| `repo` | Ecto repo for PostgreSQL persistence | Required |
| `webhook_url` | URL to send webhook notifications to | `nil` |
| `webhook_auth` | Authorization header value for webhooks | `nil` |

#### 3. Create the Database Migration

```bash
mix ecto.gen.migration add_aplyid_mock_server
```

```elixir
# priv/repo/migrations/YYYYMMDDHHMMSS_add_aplyid_mock_server.exs
defmodule MyApp.Repo.Migrations.AddAplyidMockServer do
  use Ecto.Migration

  def up, do: Aplyid.MockServer.Migrations.up(version: 1)
  def down, do: Aplyid.MockServer.Migrations.down(version: 1)
end
```

```bash
mix ecto.migrate
```

#### 4. Mount in Your Phoenix Router

```elixir
# lib/my_app_web/router.ex
forward "/aplyid-mock", Aplyid.MockServer.Router
```

#### 5. Point Your Client at the Mock Server

```elixir
config :aplyid,
  environments: [
    uat: [
      base_url: "http://localhost:4000/aplyid-mock",
      api_key: "any_key",
      api_secret: "any_secret"
    ]
  ]
```

### Verification Flow UI

When you create a transaction without a `contact_phone`, the response includes a `start_process_url` pointing to the mock verification flow. Open this URL in a browser to walk through the screens:

1. **Privacy Consent** - User confirms they accept the privacy terms
2. **Capture Photo ID** - Simulates capturing an ID document
3. **Reviewing ID Data** - Brief loading screen
4. **Check ID Details** - Displays mock extracted data for confirmation
5. **Face Verification** - Simulates the liveness check
6. **Verification Complete** - Success screen

Completing this flow automatically marks the transaction as completed with realistic mock verification data and triggers any configured webhooks.

The verification URL is generated based on the `base_url` config. When mounted in Phoenix at `/aplyid-mock`, the URL will be `http://localhost:4000/aplyid-mock/l/:id`. The router uses `conn.script_name` to correctly handle path prefixes for all internal links and redirects.

### Programmatic Completion

For automated testing, skip the UI and complete transactions programmatically:

```elixir
# Via helper function
Aplyid.MockServer.complete_transaction(transaction_id)

# Via HTTP endpoint
POST /mock/simulate/complete/:transaction_id
```

### Mock Server API Endpoints

| Endpoint | Description |
|----------|-------------|
| `POST /api/v2/send_text` | Create verification transaction |
| `PUT /api/v2/resend_text/:id` | Resend SMS notification |
| `GET /l/:id` | Start verification flow (HTML) |
| `POST /mock/simulate/complete/:id` | Simulate completion |
| `GET /health` | Health check |

### Test Setup

```elixir
# config/test.exs
config :aplyid, :mock_server,
  enabled: true,
  embedded: true,
  repo: MyApp.Repo,
  base_url: "http://localhost:4000"

config :aplyid,
  environments: [
    uat: [
      base_url: "http://localhost:4000",
      api_key: "test_key",
      api_secret: "test_secret"
    ]
  ]
```

```elixir
# In your test setup
setup do
  Aplyid.MockServer.clear_all()
  :ok
end
```

## License

MIT