stripe-servant-0.3.1.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 qualified Data.Aeson as A
import qualified Data.HashMap.Strict as HM
import Data.Maybe
import qualified Data.Text as T
import Data.Time
import Data.Time.Clock.POSIX
import qualified Data.Vector as V
import GHC.Generics
import Servant.API
import Stripe.Util.Aeson
import Text.Casing (quietSnake)
import Web.FormUrlEncoded
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,
csAllowPromotionCodes :: Maybe Bool
}
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,
cscAllowPromotionCodes :: Maybe Bool,
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 2) ''EventData)
$(deriveJSON (jsonOpts 1) ''Event)
$(deriveJSON (jsonOpts 2) ''CheckoutSession)
$(deriveJSON (jsonOpts 2) ''PriceRecurring)
$(deriveJSON (jsonOpts 1) ''Price)
$(deriveJSON (jsonOpts 2) ''Product)
$(deriveJSON (jsonOpts 2) ''SubscriptionItem)
$(deriveJSON (jsonOpts 1) ''Subscription)
$(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),
("allow_promotion_codes", maybeToList $ toUrlPiece <$> cscAllowPromotionCodes csc)
]
<> lineItems
<> pmt