Hex.pm License: MIT

Production-grade Elixir hex package for the Setu API Platform.


Installation

def deps do
  [{:setu_client, "~> 1.0"}]
end

Configuration

# config/config.exs
config :setu,
  client_id: System.get_env("SETU_CLIENT_ID"),
  client_secret: System.get_env("SETU_CLIENT_SECRET"),
  product_instance_id: System.get_env("SETU_PRODUCT_INSTANCE_ID"),
  environment: :sandbox,   # :sandbox | :production
  timeout: 30_000,
  max_retries: 3

Quick start

cfg = SetuClient.config()

# PAN verification
{:ok, result} = SetuClient.Data.KYC.PAN.verify(cfg, %{
  pan: "ABCDE1234A",
  consent: "Y",
  reason: "Customer KYC for loan onboarding"
})
SetuClient.Data.KYC.PAN.valid?(result)  # => true

# Create Dynamic QR
{:ok, qr} = SetuClient.Payments.UPI.create_dqr(cfg, merchant_id, %{
  merchant_vpa: "shop@pineaxis",
  amount: 10_000   # ₹100 in paise
})
IO.puts(qr["intentLink"])

# Account Aggregator consent
{:ok, consent} = SetuClient.Data.AA.create_consent(cfg, %{
  vua: "9999999999",
  fetch_type: "ONETIME",
  consent_types: ["TRANSACTIONS"],
  fi_types: ["DEPOSIT"],
  consent_duration: %{unit: "MONTH", value: 1},
  data_range: %{from: from_dt, to: to_dt}
})
# Redirect customer to consent["url"]

Error handling

case SetuClient.Payments.UPI.create_dqr(cfg, merchant_id, params) do
  {:ok, qr} ->
    {:ok, qr["intentLink"]}

  {:error, %SetuClient.Error{type: :validation, field: field, message: msg}} ->
    {:error, "bad #{field}: #{msg}"}

  {:error, %SetuClient.Error{type: :rate_limit, retry_after: ra}} ->
    {:retry, ra}

  {:error, %SetuClient.Error{type: :auth}} ->
    {:error, :invalid_credentials}

  {:error, err} ->
    Logger.error(SetuClient.Error.message(err))
    {:error, :setu_error}
end

Webhooks

# router.ex (Phoenix)
post "/webhooks/setu", SetuClient.Webhook.Handler, callbacks: MyApp.SetuCallbacks

# Callback module
defmodule MyApp.SetuCallbacks do
  use SetuClient.Webhook.Callbacks

  @impl SetuClient.Webhook.Callbacks
  def handle_payment(%{"eventType" => "payment.success"} = event) do
    MyApp.Orders.mark_paid(event["merchantReferenceId"])
    :ok
  end

  def handle_payment(_event), do: :ok

  @impl SetuClient.Webhook.Callbacks
  def handle_consent(event) do
    if SetuClient.Webhook.Handler.consent_active?(event) do
      MyApp.AA.create_data_session(SetuClient.Webhook.Handler.consent_id(event))
    end
    :ok
  end
end

Running linting locally

mix deps.get
mix format --check-formatted
mix credo --strict
mix dialyzer
mix test

License

MIT © 2026 Kanishka Naik