module Web.Payments.Cielo ( -- * Running the API calls runCielo , cieloConfigFromEnv , productionEnv , sandboxEnv -- * Available API Calls -- ** Generating Merchant Order IDs , getMerchantOrderId -- ** Sales , createSale , querySale , captureSale , voidSale -- *** Low-Level , updateSale -- ** Querying Sales , querySalesByMerchantOrderId , queryRecurrentSale -- ** Recurrent Sales -- *** Cancelling / Uncancelling , cancelRecurrentPayment , uncancelRecurrentPayment -- *** Updates , updateRecurrentPaymentEndDate , updateRecurrentPaymentPayment , updateRecurrentPaymentInterval , updateRecurrentPaymentInstallments , updateRecurrentPaymentCustomer , updateRecurrentPaymentRecurrencyDay , updateRecurrentPaymentNextPaymentDate , updateRecurrentPaymentNextPaymentAmount -- * Types , CieloConfig(..) , CreditCard(..) , MonadCielo , CieloError(..) , Merchant(..) , Environment(..) , Sale(..) , PaymentProvider(..) , PaymentType(..) , Currency(..) , Interval(..) , RecurrentPayment(..) , RecurrentPaymentQuery(..) , Address(..) , Customer(..) , Payment(..) , SalesByMerchantOrderQuery(..) -- * Re-exports , Default(..) , MonadIO , liftIO , FromJSON(..) , ToJSON(..) ) where import Control.Exception import Control.Monad import Control.Monad.Except import Control.Monad.Reader import Data.Aeson import Data.Convertible import Data.Default import Data.Monoid ((<>)) import Data.Text (Text, pack) import Data.UUID import Data.UUID.V4 import Network.Wreq hiding (get, post, put) import System.Environment import Web.Payments.Cielo.Types import Web.Payments.Cielo.Util -- | API Calls happen in a 'MonadCielo' type-class -- 'CieloM' is a helper instance of this class, which runs exceptions on the IO -- monad and exposes the configuration through a 'ReaderT' runCielo :: CieloConfig -> CieloM a -> IO a runCielo cnf rdr = do eret <- runExceptT (runReaderT rdr cnf) case eret of Left err -> throwIO err Right ret -> return ret -- | We can load 'CieloConfig' from the @"CIELO_MERCHANTID"@ and -- @"CIELO_MERCHANTKEY"@ environment variables cieloConfigFromEnv :: IO CieloConfig cieloConfigFromEnv = do m <- Merchant <$> (pack <$> getEnv "CIELO_MERCHANTID") <*> (pack <$> getEnv "CIELO_MERCHANTKEY") return $ CieloConfig m sandboxEnv -- | Generates a new merchant UIID getMerchantOrderId :: MonadIO m => m Text getMerchantOrderId = liftIO $ toText <$> nextRandom -- | Creates a 'Sale' createSale :: MonadCielo m => Sale -> m Sale createSale = post "/1/sales" [] -- | Queries for a 'Sale' given it's paymentId querySale :: MonadCielo m => Text -> m Sale querySale paymentId = get ("/1/sales/" <> convert paymentId) [] -- | Queries for a sales given a merchant UUID querySalesByMerchantOrderId :: MonadCielo m => Text -> m SalesByMerchantOrderQuery querySalesByMerchantOrderId merchantOrderId = get "/1/sales" [("merchantOrderId", merchantOrderId)] -- | Queries for a recurrent sale given it's paymentId queryRecurrentSale :: MonadCielo m => Text -> m RecurrentPaymentQuery queryRecurrentSale recurrentPaymentId = get ("/1/RecurrentPayment/" <> convert recurrentPaymentId) [] -- | Captures a sale captureSale :: MonadCielo m => Text -- ^ Payment ID -> Maybe Int -- ^ Amount -> Maybe Int -- ^ Service Tax Amount -> m SaleUpdate captureSale paymentId mamount mserviceTaxAmount = updateSale paymentId "capture" query where query = maybe [] (\a -> [("amount", convert (show a))]) mamount ++ maybe [] (\a -> [("serviceTaxAmount", convert (show a))]) mserviceTaxAmount -- | Voids a sale voidSale :: MonadCielo m => Text -- ^ Payment ID -> Maybe Int -- ^ Amount -> m SaleUpdate voidSale paymentId mamount = updateSale paymentId "void" query where query = maybe [] (\a -> [("amount", convert (show a))]) mamount -- | Updates a sale (Generalized PUT over a type of Sale update @/1/sales/:id/:type@) updateSale :: MonadCielo m => Text -- ^ Payment ID -> Text -- ^ :type -> [(Text, Text)] -- ^ Data to PUT -> m SaleUpdate updateSale paymentId paymentType query = put ("/1/sales/" <> convert paymentId <> "/" <> convert paymentType) query (object []) cancelRecurrentPayment :: MonadCielo m => Text -> m () cancelRecurrentPayment recurrentPaymentId = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Deactivate") [] (object []) updateRecurrentPaymentEndDate :: MonadCielo m => Text -> Text -> m () updateRecurrentPaymentEndDate recurrentPaymentId endDate = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/EndDate") [] endDate updateRecurrentPaymentInstallments :: MonadCielo m => Text -> Int -> m () updateRecurrentPaymentInstallments recurrentPaymentId installments = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Installments") [] installments updateRecurrentPaymentInterval :: MonadCielo m => Text -> Int -> m () updateRecurrentPaymentInterval recurrentPaymentId interval = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Interval") [] interval updateRecurrentPaymentRecurrencyDay :: MonadCielo m => Text -> Int -> m () updateRecurrentPaymentRecurrencyDay recurrentPaymentId dayOfMonth = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Amount") [] dayOfMonth updateRecurrentPaymentNextPaymentAmount :: MonadCielo m => Text -> Int -> m () updateRecurrentPaymentNextPaymentAmount recurrentPaymentId amount = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Amount") [] amount updateRecurrentPaymentNextPaymentDate :: MonadCielo m => Text -> Text -> m () updateRecurrentPaymentNextPaymentDate recurrentPaymentId date = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/NextPaymentDate") [] date updateRecurrentPaymentPayment :: MonadCielo m => Text -> Payment -> m () updateRecurrentPaymentPayment recurrentPaymentId payment = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Payment") [] payment uncancelRecurrentPayment :: MonadCielo m => Text -> m () uncancelRecurrentPayment recurrentPaymentId = void $ sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Reactivate") [] (object []) updateRecurrentPaymentCustomer :: MonadCielo m => Text -> Customer -> m () updateRecurrentPaymentCustomer recurrentPaymentId = void . sendRaw putWith ("/1/RecurrentPayment/" <> convert recurrentPaymentId <> "/Customer") []