defmodule Zepto.Client.Auth do @moduledoc """ Handles OAuth2 authentication for the Zepto Payments API. Zepto uses OAuth2 Authorization Code flow with refresh tokens: - Initial access is obtained via authorization code (user-initiated) - Subsequent access uses refresh tokens - Refresh tokens are single-use and non-expiring - Access tokens expire in 2 hours OAuth endpoints are on a separate host from the API: - Sandbox: `https://go.sandbox.zeptopayments.com` - Production: `https://go.zeptopayments.com` """ @token_url_path "/oauth/token" @doc """ Builds the OAuth2 token URL from the OAuth base URL. ## Examples Auth.build_token_url("https://go.sandbox.zeptopayments.com") #=> "https://go.sandbox.zeptopayments.com/oauth/token" """ @spec build_token_url(String.t()) :: String.t() def build_token_url(oauth_url) do oauth_url <> @token_url_path end @doc """ Builds the OAuth2 authorization URL for initiating the auth code flow. ## Options * `:client_id` - Your application's client ID (required) * `:redirect_uri` - Your registered redirect URI (required) * `:scope` - Space-separated scopes (required) * `:state` - Optional state parameter for CSRF protection ## Examples Auth.build_authorize_url("https://go.sandbox.zeptopayments.com", client_id: "your_client_id", redirect_uri: "https://yourapp.com/callback", scope: "public contacts payments" ) """ @spec build_authorize_url(String.t(), keyword()) :: String.t() def build_authorize_url(oauth_url, opts) do client_id = Keyword.fetch!(opts, :client_id) redirect_uri = Keyword.fetch!(opts, :redirect_uri) scope = Keyword.fetch!(opts, :scope) state = Keyword.get(opts, :state) query_params = [ {"response_type", "code"}, {"client_id", client_id}, {"redirect_uri", redirect_uri}, {"scope", scope} ] query_params = if state do query_params ++ [{"state", state}] else query_params end query_string = URI.encode_query(query_params) "#{oauth_url}/oauth/authorize?#{query_string}" end end