{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -- | -- Module : Web.Stripe.Client.Internal -- Copyright : (c) David Johnson, 2014 -- Maintainer : djohnson.m@gmail.com -- Stability : experimental -- Portability : POSIX module Web.Stripe.Client.Internal ( callAPI , stripe , Stripe , StripeRequest (..) , StripeError (..) , StripeConfig (..) , Method (..) , module Web.Stripe.Client.Util , module Web.Stripe.Client.Error ) where import Control.Exception (SomeException, try) import Control.Monad (when) import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Monad.Reader (ask, runReaderT) import Control.Monad.Trans.Either (left, runEitherT, right) import Data.Aeson (FromJSON, Value(..), eitherDecodeStrict) import Data.Monoid (mempty, (<>)) import qualified Data.Text as T import Network.Http.Client (Connection, Method (..), baselineContextSSL, buildRequest, closeConnection, concatHandler, getStatusCode, http, inputStreamBody, openConnectionSSL, receiveResponse, sendRequest, setAuthorizationBasic, setContentType, setHeader) import OpenSSL (withOpenSSL) import Web.Stripe.Client.Error (StripeError (..), StripeErrorHTTPCode (..), StripeErrorType (..)) import Web.Stripe.Client.Types (APIVersion (..), Stripe, StripeConfig (..), StripeRequest (..)) import Web.Stripe.Client.Util (fromSeconds, getParams, paramsToByteString, toBytestring, toExpandable, toMetaData, toText, toTextLower, ()) import qualified Data.ByteString as S import qualified Data.Text.Encoding as T import qualified System.IO.Streams as Streams ------------------------------------------------------------------------------ -- | Create a request to `Stripe`'s API stripe :: FromJSON a => StripeConfig -> Stripe a -> IO (Either StripeError a) stripe config requests = do withOpenSSL $ do ctx <- baselineContextSSL result <- try (openConnectionSSL ctx "api.stripe.com" 443) :: IO (Either SomeException Connection) case result of Left msg -> return $ Left $ StripeError ConnectionFailure (toText msg) Nothing Nothing Nothing Right conn -> do json <- flip runReaderT (config, conn) $ runEitherT requests closeConnection conn return json ------------------------------------------------------------------------------ -- | Debug Helper debug :: Bool debug = False ------------------------------------------------------------------------------ -- | API Request to be issued callAPI :: FromJSON a => StripeRequest -> Stripe a callAPI StripeRequest{..} = do (StripeConfig{..}, conn) <- ask let reqBody | method == GET = mempty | otherwise = paramsToByteString queryParams reqURL | method == GET = S.concat [ T.encodeUtf8 endpoint , "?" , paramsToByteString queryParams ] | otherwise = T.encodeUtf8 endpoint handleStream =<< liftIO (do req <- buildRequest $ do http method $ "/v1/" <> reqURL setAuthorizationBasic secretKey mempty setContentType "application/x-www-form-urlencoded" setHeader "Stripe-Version" (toBytestring V20141007) setHeader "Connection" "Keep-Alive" body <- Streams.fromByteString reqBody sendRequest conn req $ inputStreamBody body receiveResponse conn $ \response inputStream -> do when debug $ print response result <- concatHandler response inputStream return (response, result)) where parseFail errorMessage = left $ StripeError ParseFailure (T.pack errorMessage) Nothing Nothing Nothing unknownCode = left $ StripeError UnknownErrorType mempty Nothing Nothing Nothing handleStream (p,x) = case getStatusCode p of 200 -> case eitherDecodeStrict x of Left message -> do when debug $ liftIO $ print (eitherDecodeStrict x :: Either String Value) parseFail message Right json -> right json code | code >= 400 -> case eitherDecodeStrict x :: Either String StripeError of Left message -> parseFail message Right json -> left $ case code of 400 -> json { errorHTTP = Just BadRequest } 401 -> json { errorHTTP = Just UnAuthorized } 402 -> json { errorHTTP = Just RequestFailed } 404 -> json { errorHTTP = Just NotFound } 500 -> json { errorHTTP = Just StripeServerError } 502 -> json { errorHTTP = Just StripeServerError } 503 -> json { errorHTTP = Just StripeServerError } 504 -> json { errorHTTP = Just StripeServerError } _ -> json { errorHTTP = Just UnknownHTTPCode } _ -> unknownCode