--------------------------------------------------------------------------------G module Types where -------------------------------------------------------------------------------- import RIO import Data.Aeson import Network.HTTP.Req import Web.Internal.HttpApiData (ToHttpApiData(..)) import System.Envy (FromEnv, fromEnv, env) -------------------------------------------------------------------------------- -- | Read from .envrc data Env = Env { envGoogleClientID :: !Text , envServerPort :: !Int , envClientPort :: !Int , envStripeAPIKey :: !Text } deriving (Eq, Show) instance FromEnv Env where fromEnv _ = do envGoogleClientID <- env "GOOGLE_CLIENT_ID" envStripeAPIKey <- env "STRIPE_API_KEY" envServerPort <- env "SERVER_PORT" envClientPort <- env "CLIENT_PORT" pure Env {..} -- | Application context: a combination of Env and additional values. data Context = Context { contextGoogleClientID :: !Text , contextStripeAPIKey :: !Text , contextServerPort :: !Int , contextClientPort :: !Int } -- | Top-level except for our application, as RIO recommends defining. type Failure = () -- | When our app executes along the "happy path" this is the type of result it -- produces. type Success = () -- | This is our application monad. type AppM = RIO Context -- | The concrete type of our application. type App = AppM (Either Failure Success) data VerifyGoogleSignInRequest = VerifyGoogleSignInRequest { idToken :: !Text } deriving (Eq, Show) instance FromJSON VerifyGoogleSignInRequest where parseJSON = withObject "VerifyGoogleSignInRequest" $ \x -> do idToken <- x .: "idToken" pure VerifyGoogleSignInRequest{..} data GoogleLinkedAccount = GoogleLinkedAccount { -- { googleLinkedAccountUUID :: UUID -- , googleLinkedAccountEmail :: Email -- , googleLinkedAccountTsCreated :: Timestamp googleLinkedAccountGivenName :: !(Maybe Text) , googleLinkedAccountFamilyName :: !(Maybe Text) , googleLinkedAccountFullName :: !(Maybe Text) -- , googleLinkedAccountPictureURL :: URL -- , googleLinkedAccountLocale :: Maybe Locale } deriving (Eq, Show) data PayingCustomer = PayingCustomer { -- { payingCustomerAccountUUID :: UUID -- , payingCustomerTsCreated :: Timestamp } deriving (Eq, Show) data Session = Session { -- { sessionUUID :: UUID -- , sessionAccountUUID :: UUID -- , sessionTsCreated :: Timestamp } deriving (Eq, Show) data CurrencyCode = USD deriving (Eq, Show) instance ToJSON CurrencyCode where toJSON USD = String "usd" instance FromJSON CurrencyCode where parseJSON = withText "CurrencyCode" $ \x -> case x of "usd" -> pure USD _ -> fail "Expected a valid currency code like: \"usd\"" instance ToHttpApiData CurrencyCode where toQueryParam USD = "usd" data PaymentIntent = PaymentIntent { paymentIntentAmount :: !Int , paymentIntentCurrency :: !CurrencyCode } deriving (Eq, Show) instance ToJSON PaymentIntent where toJSON PaymentIntent{..} = object [ "amount" .= paymentIntentAmount , "currency" .= paymentIntentCurrency ] instance FromJSON PaymentIntent where parseJSON = withObject "" $ \x -> do paymentIntentAmount <- x .: "amount" paymentIntentCurrency <- x .: "currency" pure PaymentIntent{..} instance QueryParam PaymentIntent where queryParam = undefined -- All applications have their secrets... Using the secret type ensures that no -- sensitive information will get printed to the screen. newtype Secret = Secret Text deriving (Eq) instance Show Secret where show (Secret _) = "[REDACTED]" instance ToJSON Secret where toJSON (Secret x) = toJSON x instance FromJSON Secret where parseJSON = withText "Secret" $ \x -> pure $ Secret x data CreatePaymentIntentResponse = CreatePaymentIntentResponse { clientSecret :: Secret } deriving (Eq, Show) instance ToJSON CreatePaymentIntentResponse where toJSON CreatePaymentIntentResponse{..} = object [ "clientSecret" .= clientSecret ] data StripePaymentIntent = StripePaymentIntent { pmtIntentClientSecret :: Secret } deriving (Eq, Show) instance FromJSON StripePaymentIntent where parseJSON = withObject "StripeCreatePaymentIntentResponse" $ \x -> do pmtIntentClientSecret <- x .: "client_secret" pure StripePaymentIntent{..}