stripe-0.8.1: src/Web/Stripe/Plan.hs
{-# LANGUAGE OverloadedStrings #-}
module Web.Stripe.Plan
( Plan(..)
, amount
, PlanInterval(..)
, PlanId(..)
, PlanTrialDays(..)
, createPlan
, getPlan
, getPlans
, delPlan
, delPlanById
{- Re-Export -}
, Amount(..)
, Count(..)
, Currency(..)
, Offset(..)
, StripeConfig(..)
, StripeT(StripeT)
, runStripeT
) where
import Control.Applicative ((<$>), (<*>))
import Control.Monad (liftM, mzero)
import Control.Monad.Error (MonadIO)
import Data.Aeson (FromJSON (..), Value (..), (.:), (.:?))
import Data.Char (toLower)
import qualified Data.Text as T
import Network.HTTP.Types (StdMethod (..))
import Web.Stripe.Client (StripeConfig (..), StripeRequest (..),
StripeT (..), baseSReq, query, queryData,
query_, runStripeT)
import Web.Stripe.Utils (Amount (..), Count (..), Currency (..),
Offset (..), optionalArgs, showByteString,
textToByteString)
----------------
-- Data Types --
----------------
-- | Represents a plan in the Stripe system.
data Plan = Plan
{ planId :: PlanId
, planAmount :: Amount
, planInterval :: PlanInterval
, planName :: T.Text
, planCurrency :: Currency
, planTrialDays :: Maybe PlanTrialDays
} deriving Show
-- | Represents the billing cycle for a plan. If an interval identifier is not
-- known, 'UnknownPlan' is used to carry the original identifier supplied by
-- Stripe.
data PlanInterval = Monthly | Yearly | UnknownPlan T.Text deriving (Show, Eq)
-- | Represents the identifier for a given 'Plan' in the Stripe system.
newtype PlanId = PlanId { unPlanId :: T.Text } deriving (Show, Eq)
-- | Represents the length of the trial period. That is, the number of days
-- before the customer is billed.
newtype PlanTrialDays = PlanTrialDays { unPlanTrialDays :: Int } deriving (Show, Eq)
amount :: Plan -> Int
amount plan = unAmount $ planAmount plan
-- | Creates a 'Plan' in the Stripe system.
createPlan :: MonadIO m => Plan -> StripeT m ()
createPlan p = query_ (planRq []) { sMethod = POST, sData = fdata }
where
fdata = pdata ++ optionalArgs odata
pdata = [ ("id", textToByteString . unPlanId $ planId p)
, ("amount", showByteString $ amount p)
, ("interval", textToByteString . fromPlanInterval $ planInterval p)
, ("name", textToByteString $ planName p)
, ("currency", textToByteString . unCurrency $ planCurrency p)
]
odata = [ ( "trial_period_days"
, showByteString . unPlanTrialDays <$> planTrialDays p
)
]
-- | Retrieves a specific 'Plan' based on its 'PlanId'.
getPlan :: MonadIO m => PlanId -> StripeT m Plan
getPlan (PlanId pid) = liftM snd $ query (planRq [pid])
-- | Retrieves a list of all 'Plan's. The query can optionally be refined to
-- a specific:
--
-- * number of charges, via 'Count' and
-- * page of results, via 'Offset'.
getPlans :: MonadIO m => Maybe Count -> Maybe Offset -> StripeT m [Plan]
getPlans mc mo = liftM snd $ queryData (planRq []) { sQString = qs }
where
qs = optionalArgs [ ("count", show . unCount <$> mc)
, ("offset", show . unOffset <$> mo)
]
-- | Deletes a 'Plan' if it exists. If it does not, an 'InvalidRequestError'
-- will be thrown indicating this.
delPlan :: MonadIO m => Plan -> StripeT m Bool
delPlan = delPlanById . planId
-- | Deletes a 'Plan', identified by its 'PlanId', if it exists. If it does
-- not, an 'InvalidRequestError' will be thrown indicating this.
delPlanById :: MonadIO m => PlanId -> StripeT m Bool
delPlanById (PlanId pid) = liftM snd $ queryData (planRq [pid]) { sMethod = DELETE }
-- | Convenience function to create a 'StripeRequest' specific to plan-related
-- actions.
planRq :: [T.Text] -> StripeRequest
planRq pcs = baseSReq { sDestination = "plans":pcs }
------------------
-- JSON Parsing --
------------------
-- | Converts a 'PlanInterval' to a T.Text for input into the Stripe API. For
-- 'UnknownPlan's, the original interval code will be used.
fromPlanInterval :: PlanInterval -> T.Text
fromPlanInterval Monthly = "month"
fromPlanInterval Yearly = "year"
fromPlanInterval (UnknownPlan p) = p
-- | Convert a T.Text to a 'PlanInterval'. Used for parsing output from the
-- Stripe API.
toPlanInterval :: T.Text -> PlanInterval
toPlanInterval p = case T.map toLower p of
"month" -> Monthly
"year" -> Yearly
_ -> UnknownPlan p
-- | Attempts to parse JSON into a 'Plan'.
instance FromJSON Plan where
parseJSON (Object o) = Plan
<$> (PlanId <$> o .: "id")
<*> (Amount <$> o .: "amount")
<*> (toPlanInterval <$> o .: "interval")
<*> o .: "name"
<*> (Currency <$> o .: "currency")
<*> (fmap PlanTrialDays <$> o .:? "trial_period_days")
parseJSON _ = mzero