-- | -- Module: Network.Payments.PayPal.Types.Transaction -- Copyright: (C) 2016 Braden Walters -- License: MIT (see LICENSE file) -- Maintainer: Braden Walters -- Stability: experimental -- Portability: ghc {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Network.Payments.PayPal.Types.Transaction ( Details(..) , Amount(..) , Item(..) , ItemList(..) , Transaction(..) ) where #if __GLASGOW_HASKELL__ < 710 import Control.Applicative #endif import Control.Monad import Data.Aeson import Data.Aeson.Types import Data.Maybe import qualified Data.Text as T import Network.Payments.PayPal.Types.Address import Network.Payments.PayPal.Types.Currency -- |Details about the amount of a transaction. data Details = Details { detailsShipping :: MonetaryAmount , detailsSubtotal :: MonetaryAmount , detailsTax :: MonetaryAmount } deriving (Eq, Show) instance ToJSON Details where toJSON details = object ["shipping" .= detailsShipping details, "subtotal" .= detailsSubtotal details, "tax" .= detailsTax details] instance FromJSON Details where parseJSON (Object obj) = Details <$> obj .: "shipping" <*> obj .: "subtotal" <*> obj .: "tax" parseJSON _ = mzero -- |Amount of a transaction and its currency. The details must sum up to the -- total or the request is rejected. data Amount = Amount { amountCurrency :: Currency , amountTotal :: MonetaryAmount , amountDetails :: Details } deriving (Eq, Show) instance ToJSON Amount where toJSON amt = object ["currency" .= amountCurrency amt, "total" .= amountTotal amt, "details" .= amountDetails amt] instance FromJSON Amount where parseJSON (Object obj) = Amount <$> obj .: "currency" <*> obj .: "total" <*> obj .: "details" parseJSON _ = mzero -- |An individual item being purchased. data Item = Item { itemQuantity :: Integer , itemName :: String , itemPrice :: MonetaryAmount , itemCurrency :: Currency , itemSku :: String , itemDescription :: Maybe String } deriving (Eq, Show) instance ToJSON Item where toJSON item = object (["quantity" .= itemQuantity item, "name" .= itemName item, "price" .= itemPrice item, "currency" .= itemCurrency item, "sku" .= itemSku item] ++ maybeToList (("description" .=) <$> itemDescription item)) instance FromJSON Item where parseJSON (Object obj) = Item <$> (obj .: "quantity" >>= parseFuzzyJSONInt) <*> obj .: "name" <*> obj .: "price" <*> obj .: "currency" <*> obj .: "sku" <*> obj .:? "description" parseJSON _ = mzero -- |A list of items being purchased and the shipping address if one exists. data ItemList = ItemList { itemListItems :: [Item] , itemListShippingAddress :: Maybe ShippingAddress } deriving (Eq, Show) instance ToJSON ItemList where toJSON list = object (["items" .= itemListItems list] ++ maybeToList (("shipping_address" .=) <$> itemListShippingAddress list)) instance FromJSON ItemList where parseJSON (Object obj) = ItemList <$> obj .: "items" <*> obj .:? "shipping_address" parseJSON _ = mzero -- |Details about a financial transaction over PayPal. data Transaction = Transaction { transactAmount :: Amount , transactDescription :: Maybe String , transactItemList :: ItemList } deriving (Eq, Show) instance ToJSON Transaction where toJSON trans = object (["amount" .= transactAmount trans, "item_list" .= transactItemList trans] ++ maybeToList (("description" .=) <$> transactDescription trans)) instance FromJSON Transaction where parseJSON (Object obj) = Transaction <$> obj .: "amount" <*> obj .:? "description" <*> obj .: "item_list" parseJSON _ = mzero -- |This takes either a string or a number and tries to produce an integer out -- of it. This is necessary because PayPal apparently returns different types -- for the same data depending on the usage... parseFuzzyJSONInt :: (Integral a, Read a) => Value -> Parser a parseFuzzyJSONInt (String txt) = return $ read $ T.unpack txt parseFuzzyJSONInt (Number num) = return $ round num parseFuzzyJSONInt _ = mzero