packages feed

api-monobank-0.1.3.0: src/Monobank/Api.hs

{-# LANGUAGE DataKinds                  #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE FlexibleInstances          #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE TemplateHaskell            #-}
{-# LANGUAGE TypeFamilies               #-}
{-# LANGUAGE TypeOperators              #-}

module Monobank.Api
    ( getCurrencies
    , getCurrencyRates'
    , getPersonalInfo'
    , getPersonalInfoSigned'
    , getPersonalStatement'
    , getPersonalStatementSigned'
    , getPersonalStatementFull
    ) where

import           Control.Applicative
import           Control.Monad
import           Control.Monad.IO.Class
import           Data.Aeson
import qualified Data.ByteString.Char8      as B
import qualified Data.ByteString.Lazy.Char8 as BL
import           Data.Monoid
import           Data.Proxy
import qualified Data.Text                  as T
import           Data.Time
import           Data.Time.Clock.POSIX
import           GHC.Generics
import           Network.HTTP.Client        (Manager, newManager)
import qualified Network.HTTP.Client        as C
import           Network.HTTP.Client.TLS    (tlsManagerSettings)
import           Network.HTTP.Conduit       (simpleHttp)
import qualified Network.HTTP.Simple        as S
import           Servant.API
import           Servant.Client

import           Monobank.Types
import           Monobank.Utils

--------------------------------------------------------------------------------

endpoint = "api.monobank.ua"

type MonobankAPI =
       "bank"
    :> "currency"
    :> Get '[JSON] [CurrencyPair]

   -- GET /personal/client-info
  :<|> "personal"
    :> "client-info"
    :> Header "X-Token" T.Text
    :> Get '[JSON] User

  -- GET /personal/statement/{account}/{from}/{to}
  :<|> "personal"
    :> "statement"
    :> Header  "X-Token" Token
    :> Capture "account" T.Text
    :> Capture "from"    T.Text
    :> Capture "to"      T.Text
    :> Get '[JSON] Statement

monobankAPI :: Proxy MonobankAPI
monobankAPI = Proxy

-- Derive call functions for the api
getCurrencyRates     :: ClientM [CurrencyPair]
getPersonalInfo      :: Maybe T.Text -> ClientM User
getPersonalStatement :: Maybe T.Text -> T.Text -> T.Text -> T.Text -> ClientM Statement
(     getCurrencyRates
 :<|> getPersonalInfo
 :<|> getPersonalStatement ) = client monobankAPI

--------------------------------------------------------------------------------
-- Corporate API description https://api.monobank.ua/docs/corporate.html

type MonobankCorporateAPI =

  -- POST /personal/auth/request
       "personal"
    :> "auth"
    :> "request"
    :> Header  "X-Key-Id"      T.Text
    :> Header  "X-Time"        T.Text
    :> Header  "X-Permissions" T.Text
    :> Header  "X-Sign"        T.Text
    :> Header  "X-Callback"    T.Text
    :> Post '[JSON] (Either String TokenRequest)

  -- Get /personal/auth/request
  :<|> "personal"
    :> "auth"
    :> "request"
    :> Header  "X-Key-Id"      T.Text
    :> Header  "X-Time"        T.Text
    :> Header  "X-Permissions" T.Text
    :> Header  "X-Sign"        T.Text
    :> Get '[JSON] (Either String TokenRequest)

  -- GET /personal/client-info
  :<|> "personal"
    :> "client-info"
    :> Header  "X-Key-Id"      T.Text
    :> Header  "X-Time"        T.Text
    :> Header  "X-Permissions" T.Text
    :> Header  "X-Sign"        T.Text
    :> Get '[JSON] User

  -- GET /personal/statement/{account}/{from}/{to}
  :<|> "personal"
    :> "statement"
    :> Header  "X-Key-Id"      T.Text
    :> Header  "X-Time"        T.Text
    :> Header  "X-Permissions" T.Text
    :> Header  "X-Sign"        T.Text
    :> Capture "account"       T.Text
    :> Capture "from"          T.Text
    :> Capture "to"            T.Text
    :> Get '[JSON] Statement

monobankCorporateAPI :: Proxy MonobankCorporateAPI
monobankCorporateAPI = Proxy

-- Derive call functions for the api
initializeAuth             :: Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> ClientM (Either String TokenRequest)
requestAuth                :: Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> ClientM (Either String TokenRequest)
getPersonalInfoSigned      :: Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> ClientM User
getPersonalStatementSigned :: Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> Maybe T.Text -> T.Text -> T.Text -> T.Text -> ClientM Statement
(     initializeAuth
 :<|> requestAuth
 :<|> getPersonalInfoSigned
 :<|> getPersonalStatementSigned ) = client monobankCorporateAPI

--------------------------------------------------------------------------------

-- | Get a basic list of monobank currency rates
-- |
getCurrencyRates' :: IO (Either ServantError [CurrencyPair])
getCurrencyRates' = do
  mgr <- newManager tlsManagerSettings
  let env = ClientEnv mgr host Nothing
  runClientM getCurrencyRates env
  where
    host = (BaseUrl Https endpoint 443 "")

-- | Ger bank user personal information
-- |
getPersonalInfo' :: Maybe Token                        -- ^ unique user token
                 -> IO (Either ServantError User)
getPersonalInfo' tkn = do
  mgr <- newManager tlsManagerSettings
  let env = ClientEnv mgr host Nothing
  runClientM (getPersonalInfo tkn) env
    where
      host = (BaseUrl Https endpoint 443 "")


-- | Receive an extract for the time from {to} to {to} time in seconds Unix time format.
-- | The maximum time for which it is possible to extract an extract is 31 days
-- | (2678400 seconds) Limit on the use of the function no more than 1 time in 60 seconds.
--
getPersonalStatement' :: Maybe Token
                      -> T.Text
                      -> T.Text
                      -> T.Text
                      -> IO (Either ServantError Statement)
getPersonalStatement' tkn accM fromM toM = do
  mgr <- newManager tlsManagerSettings
  let env = ClientEnv mgr host Nothing
  runClientM (getPersonalStatement tkn accM fromM toM) env
  where
    host = (BaseUrl Https endpoint 443 "")

getPersonalStatement'' :: Maybe Token
                       -> T.Text
                       -> T.Text
                       -> T.Text
                       -> IO (Either ServantError Statement)
getPersonalStatement'' tknM acc from to = do
  mgr <- newManager tlsManagerSettings
  let env   = ClientEnv mgr host Nothing
      from' = T.init . T.pack . show . utcTimeToPOSIXSeconds $ (read (T.unpack from) :: UTCTime)
      to'   = T.init . T.pack . show . utcTimeToPOSIXSeconds $ (read (T.unpack to)   :: UTCTime)
  putStrLn $ show from'
  runClientM (getPersonalStatement tknM acc from' to') env
    where
      host  = (BaseUrl Https endpoint 443 "")

getPersonalInfoSigned' = undefined

getPersonalStatementSigned' = undefined

--------------------------------------------------------------------------------

-- Get a basic list of monobank currency rates.
-- Information is cached and updated no more than once every 5 minutes.
--
-- GET /bank/currency
getCurrencies :: IO [CurrencyPair]
getCurrencies = do
  let path = T.concat [T.pack endpoint, "/bank/currency"]
  -- results <- fmap decodeEither $ simpleHttp (T.unpack path)
  resp <- simpleHttp (T.unpack path)
  --putStrLn $ show $ resp
  let res = eitherDecode resp
  case res of
    Left err  -> do
      putStrLn err
      return $ []
    Right val -> do
      return $ val

-- Get Personal Statement from beginning
-- 12 months will take 12 minutes,
getPersonalStatementFull :: Maybe Token
                         -> T.Text
                         -> T.Text
                         -> T.Text
                         -> IO (Either String Statement)
getPersonalStatementFull tknM acc from to = do
  let estimatedDownloadTime = 12 -- due to API restrictions, we can make only 1 call per minute
  putStrLn $ "monobank-api: due to API restrictions, we can make only 1 call per minute"
  putStrLn $ "              operation will some time, approx ~ " ++ (show estimatedDownloadTime) ++ " min"

  -- TODO: do actual calls respetively to API restrictions

  return $ Left "Error"