stripe-servant-0.1.2.0: src/Stripe/Resources.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
module Stripe.Resources
( -- * Core Types
TimeStamp(..), StripeList(..)
-- * Customers
, CustomerId(..), Customer(..), CustomerCreate(..), CustomerUpdate(..)
-- * Product catalog
, ProductId(..), PriceId(..)
, Product(..), ProductCreate(..)
, Price(..), PriceRecurring(..), PriceCreate(..), PriceCreateRecurring(..)
-- * Subscriptions
, SubscriptionId(..), SubscriptionItemId(..), Subscription(..), SubscriptionItem(..), SubscriptionCreate(..), SubscriptionCreateItem(..)
-- * Customer Portal
, CustomerPortalId(..), CustomerPortal(..), CustomerPortalCreate(..)
-- * Checkout
, CheckoutSessionId(..), CheckoutSession(..), CheckoutSessionCreate(..), CheckoutSessionCreateLineItem(..)
-- * Events
, EventId(..), Event(..), EventData(..)
)
where
import Stripe.Util.Aeson
import Data.Maybe
import Data.Time
import Data.Time.Clock.POSIX
import GHC.Generics
import Servant.API
import Text.Casing (quietSnake)
import Web.FormUrlEncoded
import qualified Data.Aeson as A
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
import qualified Data.Vector as V
formOptions :: Int -> FormOptions
formOptions x =
FormOptions
{ fieldLabelModifier = quietSnake . drop x }
-- | A 'UTCTime' wrapper that has unix timestamp JSON representation
newtype TimeStamp
= TimeStamp { unTimeStamp :: UTCTime }
deriving (Show, Eq)
instance A.ToJSON TimeStamp where
toJSON = A.Number . fromRational . toRational . utcTimeToPOSIXSeconds . unTimeStamp
instance A.FromJSON TimeStamp where
parseJSON =
A.withScientific "unix timestamp" $ \sci ->
pure $ TimeStamp $ posixSecondsToUTCTime (fromRational $ toRational sci)
instance ToHttpApiData TimeStamp where
toUrlPiece x =
let unix :: Int
unix = round . utcTimeToPOSIXSeconds . unTimeStamp $ x
in T.pack (show unix)
-- | A 'V.Vector' wrapper with an indication is there are more items available through pagination.
data StripeList a
= StripeList
{ slHasMore :: Bool
, slData :: V.Vector a
} deriving (Show, Eq, Functor)
instance Semigroup (StripeList a) where
(<>) a b = StripeList (slHasMore a || slHasMore b) (slData a <> slData b)
instance Monoid (StripeList a) where
mempty = StripeList False mempty
instance Applicative StripeList where
pure = StripeList False . pure
(<*>) go x = StripeList (slHasMore go || slHasMore x) (slData go <*> slData x)
newtype CustomerId
= CustomerId { unCustomerId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data Customer
= Customer
{ cId :: CustomerId
, cLivemode :: Bool
, cCreated :: TimeStamp
, cName :: Maybe T.Text
, cEmail :: Maybe T.Text
} deriving (Show, Eq)
data CustomerCreate
= CustomerCreate
{ ccName :: Maybe T.Text
, ccEmail :: Maybe T.Text
} deriving (Show, Eq, Generic)
data CustomerUpdate
= CustomerUpdate
{ cuName :: Maybe T.Text
, cuEmail :: Maybe T.Text
} deriving (Show, Eq, Generic)
newtype EventId
= EventId { unEventId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data Event
= Event
{ eId :: EventId
, eCreated :: TimeStamp
, eLivemode :: Bool
, eType :: T.Text
, eApiVersion :: T.Text
, eData :: EventData
} deriving (Show, Eq)
data EventData
= EventData
{ edObject :: A.Value
} deriving (Show, Eq)
newtype PriceId
= PriceId { unPriceId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data Price
= Price
{ pId :: PriceId
, pActive :: Bool
, pCurrency :: T.Text
, pNickname :: Maybe T.Text
, pType :: T.Text -- TODO: make enum
, pRecurring :: Maybe PriceRecurring
, pUnitAmount :: Maybe Int
, pProduct :: ProductId
, pLookupKey :: Maybe T.Text
} deriving (Show, Eq)
data PriceRecurring
= PriceRecurring
{ prInterval :: T.Text -- TODO: make enum
, prIntervalCount :: Int
} deriving (Show, Eq)
data PriceCreate
= PriceCreate
{ pcCurrency :: T.Text
, pcUnitAmount :: Maybe Int
, pcProduct :: ProductId
, pcLookupKey :: Maybe T.Text
, pcTransferLookupKey :: Bool
, pcRecurring :: Maybe PriceCreateRecurring
} deriving (Show, Eq, Generic)
data PriceCreateRecurring
= PriceCreateRecurring
{ prcInterval :: T.Text -- TODO: make enum
, prcIntervalCount :: Maybe Int
} deriving (Show, Eq)
newtype ProductId
= ProductId { unProductId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data Product
= Product
{ prId :: ProductId
, prActive :: Bool
, prName :: T.Text
, prDescription :: Maybe T.Text
} deriving (Show, Eq)
data ProductCreate
= ProductCreate
{ prcName :: T.Text
, prcDescription :: Maybe T.Text
} deriving (Show, Eq, Generic)
newtype SubscriptionId
= SubscriptionId { unSubscriptionId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data Subscription
= Subscription
{ sId :: SubscriptionId
, sCancelAtPeriodEnd :: Bool
, sCurrentPeriodEnd :: TimeStamp
, sCurrentPeriodStart :: TimeStamp
, sCustomer :: CustomerId
, sItems :: StripeList SubscriptionItem
, sStatus :: T.Text -- TODO: make enum
} deriving (Show, Eq)
newtype SubscriptionItemId
= SubscriptionItemId { unSubscriptionItemId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data SubscriptionItem
= SubscriptionItem
{ siId :: SubscriptionItemId
, siPrice :: Price
, siQuantity :: Maybe Int
, siSubscription :: SubscriptionId
} deriving (Show, Eq)
data SubscriptionCreateItem
= SubscriptionCreateItem
{ sciPrice :: PriceId
, sciQuantity :: Maybe Int
} deriving (Show, Eq, Generic)
data SubscriptionCreate
= SubscriptionCreate
{ scCustomer :: CustomerId
, scItems :: [SubscriptionCreateItem]
, scCancelAtPeriodEnd :: Maybe Bool
, scTrialEnd :: Maybe TimeStamp
} deriving (Show, Eq, Generic)
newtype CheckoutSessionId
= CheckoutSessionId { unCheckoutSessionId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data CheckoutSession
= CheckoutSession
{ csId :: CheckoutSessionId
, csLivemode :: Bool
, csClientReferenceId :: Maybe T.Text
, csCancelUrl :: T.Text
, csSuccessUrl :: T.Text
, csPaymentMethodTypes :: V.Vector T.Text -- TODO: make enum
, csSubscription :: Maybe SubscriptionId
} deriving (Show, Eq)
data CheckoutSessionCreate
= CheckoutSessionCreate
{ cscCancelUrl :: T.Text
, cscMode :: T.Text -- TODO: make enum
, cscPaymentMethodTypes :: [T.Text] -- TODO: make enum
, cscSuccessUrl :: T.Text
, cscClientReferenceId :: Maybe T.Text
, cscCustomer :: Maybe CustomerId
, cscLineItems :: [CheckoutSessionCreateLineItem]
} deriving (Show, Eq, Generic)
data CheckoutSessionCreateLineItem
= CheckoutSessionCreateLineItem
{ cscliPrice :: PriceId
, cscliQuantity :: Integer
} deriving (Show, Eq, Generic)
newtype CustomerPortalId
= CustomerPortalId { unCustomerPortalId :: T.Text }
deriving (Show, Eq, ToJSON, FromJSON, ToHttpApiData)
data CustomerPortal
= CustomerPortal
{ cpId :: CustomerPortalId
, cpLivemode :: Bool
, cpCreated :: TimeStamp
, cpCustomer :: CustomerId
, cpReturnUrl :: Maybe T.Text
, cpUrl :: T.Text
} deriving (Show, Eq)
data CustomerPortalCreate
= CustomerPortalCreate
{ cpcCustomer :: CustomerId
, cpcReturnUrl :: Maybe T.Text
} deriving (Show, Eq, Generic)
$(deriveJSON (jsonOpts 2) ''StripeList)
$(deriveJSON (jsonOpts 1) ''Customer)
$(deriveJSON (jsonOpts 1) ''Event)
$(deriveJSON (jsonOpts 2) ''EventData)
$(deriveJSON (jsonOpts 2) ''CheckoutSession)
$(deriveJSON (jsonOpts 1) ''Price)
$(deriveJSON (jsonOpts 2) ''PriceRecurring)
$(deriveJSON (jsonOpts 2) ''Product)
$(deriveJSON (jsonOpts 1) ''Subscription)
$(deriveJSON (jsonOpts 2) ''SubscriptionItem)
$(deriveJSON (jsonOpts 2) ''CustomerPortal)
instance ToForm CustomerCreate where
toForm = genericToForm (formOptions 2)
instance ToForm CustomerUpdate where
toForm = genericToForm (formOptions 2)
instance ToForm CustomerPortalCreate where
toForm = genericToForm (formOptions 3)
instance ToForm ProductCreate where
toForm = genericToForm (formOptions 3)
instance ToForm PriceCreate where
toForm pc =
let recurringPiece =
case pcRecurring pc of
Nothing -> []
Just x ->
[ ("recurring[interval]", [prcInterval x])
, ("recurring[interval_count]", maybeToList $ fmap toUrlPiece $ prcIntervalCount x)
]
in Form $ HM.fromList $
[ ("currency", [pcCurrency pc])
, ("product", [toUrlPiece $ pcProduct pc])
, ("unit_amount", maybeToList $ fmap toUrlPiece $ pcUnitAmount pc)
, ("lookup_key", maybeToList $ pcLookupKey pc)
, ("transfer_lookup_key", [toUrlPiece $ pcTransferLookupKey pc])
] <> recurringPiece
instance ToForm SubscriptionCreate where
toForm sc =
let convertItem (idx, itm) =
[ ("items[" <> toUrlPiece idx <> "][price]", [toUrlPiece $ sciPrice itm])
, ("items[" <> toUrlPiece idx <> "][quantity]", maybeToList $ toUrlPiece <$> sciQuantity itm)
]
lineItems =
concatMap convertItem (zip ([0..] :: [Int]) (scItems sc))
in Form $ HM.fromList $
[ ("customer", [toUrlPiece $ scCustomer sc])
, ("cancel_at_period_end", maybeToList $ toUrlPiece <$> scCancelAtPeriodEnd sc)
, ("trial_end", maybeToList $ toUrlPiece <$> scTrialEnd sc)
] <> lineItems
instance ToForm CheckoutSessionCreate where
toForm csc =
let convertItem (idx, itm) =
[ ("line_items[" <> toUrlPiece idx <> "][price]", [toUrlPiece $ cscliPrice itm])
, ("line_items[" <> toUrlPiece idx <> "][quantity]", [toUrlPiece $ cscliQuantity itm])
]
lineItems =
concatMap convertItem (zip ([0..] :: [Int]) (cscLineItems csc))
convertPmt (idx, pm) =
( "payment_method_types[" <> toUrlPiece idx <> "]"
, [pm]
)
pmt =
map convertPmt (zip ([0..] :: [Int]) (cscPaymentMethodTypes csc))
in Form $ HM.fromList $
[ ("cancel_url", [cscCancelUrl csc])
, ("success_url", [cscSuccessUrl csc])
, ("mode", [cscMode csc])
, ("client_reference_id", maybeToList $ cscClientReferenceId csc)
, ("customer", maybeToList $ fmap toUrlPiece $ cscCustomer csc)
] <> lineItems <> pmt