Adds data structures for payments system. Adds a billing-related table creation migration.
DFOBMSAODB3NKW37B272ZXA2ML5HMIH3N3C4GT2DPEQS7ZFK4SNAC NAS4BFL43SIUAAC663R6VFBHQ2DKI45K6Y6ZKVQI7S5547HBAN7QC AXKKXBWN4EMUOLV43WN52JSKJPBV7TLSGLNJW5EZXHSJNKCYUWOQC ADMKQQGCGVSHHIMVQ4XFRDCG544SBJCYALSKZV45CQQBZ4ACUH2AC POX3UAMTGCNS3SU5I6IKDNCCSHEAUSFBZ3WCMQ3EXKPNXETOI6BAC EZQG2APB36DDMIAYDPPDGOIXOD7K2RZZSGC2NKGZIHB2HZBTW7EQC BROSTG5KP3NUNLSYPVQID254TE47E5RKQAKLPIY7PGWETE6JNMTAC 4U7F3CPIDTK6JSEDMNMHVKSR7HOQDLZQD2PPVMDLHO5SFSIMUXZAC 64C6AWH66FDKU6UE6Z6JPX2J2GBM2JOPTH2GL6LHKAIUBGNGDZ5AC HALRDT2F22DAQ54M334YSKD2XJCAXDXEZPDKGMOWKH7XELBAS6MAC 2XQD6KKKD6QVHFHAEMVE3XXY7X2T7BLTLL7XIILZAXNJJH2YONUQC KEP5WUFJXTMKRRNZLYTGYYWA4VLFCMHTKTJYF5EA5IWBYFMU6WYQC ZP62WC472OTQETO2HTHIQIPO57XZIWVKPA4KL62GYU4OZDMB6NSAC WO2MINIF4TXOHWSE7JWXRZYN64XRVLYIRFMF4SMPSOXKA2V77KMQC WZUHEZSBRKHQMNWDKVG4X6DDIQEAXTGI6IGAJ5ERPRQ3W2KUMX4QC NEDDHXUK3GNFMOFO3KLU7NRIHCTYNWBT3D6HTKZAOXFDE6HMDZ6AC IZEVQF627FA7VV25KJAWYWGGC35LZUUBBQRPN5ZAINTQLPEDRTEAC RN7EI6INGUUHGMNY5RU3NH56WPLRZY5ZMYDNFMNE3TGV4ESFLQIAC GCVQD44VRPQVKPZEPIC4AOIXLJIG2ZMV3QI2Y7KALUT6NVUBSGSAC 3QVT6MA6I2CILQH3LUZABS4YQ7MN6CNRYTDRVS376OOHTPLYTFJAC QMRKFEPGFBCEWAIXPEIG5ILKAJ2JH5L3TOITHR4HNJXK5FN3KXBQC TNR3TEHKVADAEZSTOD2XLSUTSW5AWST2YUW4CWK5KE7DSC6XHZNAC O5FVTOM6YFBLEPF3S576K6IMT6ZZ5VQCSB3YVXNS4CKBITKCRZ7AC NLZ3JXLOOIL37O3RRQWXHNPNSNEOOLPD6MCB754BEBECQB3KGR2AC ASF3UPJLCX7KIUCNJD5KAXSPDXCUEJHLL4HBRTRUBPCW73IXOCWQC BWN72T44GRRZ6K2OPN56FTLNEB7J7AGC7T2U5HSMLEKUPGJP2NUAC W35DDBFYF6Z4ZPCFEO5RPAONZLYCSNTXUSTS6FIUUVZHCI6Q7GHAC Z3MK2PJ5U222DXRS22WCDHVPZ7HVAR3HOCUNXIGX6VMEPBQDF6PQC 7VGYLTMURLVSVUYFW7TCRZTDQ6RE2EPSPPA43XKHDOBFWYVVSJHQC NVOCQVASZWTKQJG7GPH7KHKZZR7NUG4WLV5YY4KAIRPCJRWCZPIAC 75N3UJ4JK56KXF56GASGPAWLFYGJDETVJNYTF4KXFCQM767JUU5AC Z7KS5XHHC6PAMTVBHXY7KUSS3BWAOU6FSYIITUCFOOJZU4OUJHBAC 2WOOGXDHVQ6L2MQYUTLJ6H6FVSQNJN6SMJL5DG7HAHFYPJLRT2SAC BXGLKYRXO2O4NRM3BLNWQ7AWVPQXAMFS57MFYHJNOZZEZZW5BH6AC {-# LANGUAGE TemplateHaskell #-}module Aftok.Billables whereimport ClassyPreludeimport Control.Lens (makeLenses)import Data.UUIDimport Aftok.Time (Days(..))newtype BillableId = BillableId UUID deriving (Show, Eq)data BillingFrequency= Annually| Monthly Int| SemiMonthly| Weekly IntmakeLenses ''BillingFrequencydata Recurrence= Recurring { _frequency :: BillingFrequency }| OneTimemakeLenses ''Recurrencedata Billable (p :: *) (c :: *) = Billable{ _project :: p, _name :: Text, _description :: Text, _recurrence :: Recurrence, _amount :: c, _gracePeriod :: Days}makeLenses ''Billablemonthly :: BillingFrequencymonthly = Monthly 1bimonthly :: BillingFrequencybimonthly = Monthly 2quarterly :: BillingFrequencyquarterly = Monthly 3seminannually :: BillingFrequencyseminannually = Monthly 6annually :: BillingFrequencyannually = Annually
btcAddrParser f v = BtcAddr <$> fromField f v
btcAddrParser f v = doaddrMay <- parseBtcAddr <$> fromField f vlet err = ConversionFailed { errSQLType = "text", errSQLTableOid = tableOid f, errSQLField = maybe "" B.unpack (name f), errHaskellType = "BtcAddr", errMessage = "could not deserialize value to a valid BTC address"}maybe (conversionError err) pure addrMay
let err = UnexpectedNull (B.unpack tn)(tableOid f)(maybe "" B.unpack (name f))"UTCTime -> LogEvent""columns of type event_t should not contain null values"
let err = UnexpectedNull { errSQLType = B.unpack tn, errSQLTableOid = tableOid f, errSQLField = maybe "" B.unpack (name f), errHaskellType = "UTCTime -> LogEvent", errMessage = "columns of type event_t should not contain null values"}
let err = Incompatible (B.unpack tn)(tableOid f)(maybe "" B.unpack (name f))"UTCTime -> LogEvent""column was not of type event_t"
let err = Incompatible { errSQLType = B.unpack tn, errSQLTableOid = tableOid f, errSQLField = maybe "" B.unpack (name f), errHaskellType = "UTCTime -> LogEvent", errMessage = "column was not of type event_t"}
CreateBillable :: Billable ProjectId Satoshi -> DBOp BillableIdReadBillable :: BillableId -> DBOp (Maybe (Billable ProjectId Satoshi))CreatePaymentRequest :: UserId -> PaymentRequest ProjectId BillableId -> DBOp PaymentRequestIdCreatePayment :: Payment PaymentRequestId UserId -> DBOp PaymentId
createBillable :: UserId -> Billable ProjectId Satoshi -> DBProg BillableIdcreateBillable uid b = withProjectAuth (b ^. B.project) uid $ CreateBillable breadBillable :: BillableId -> DBProg (Maybe (Billable ProjectId Satoshi))readBillable = fc . ReadBillable--createPaymentRequest :: BillableId -> DBProg PaymentRequestId--createPaymentRequest bid = do-- billable <- readBillable bidreadPaymentHistory :: UserId -> DBProg [Payment PaymentRequestId UserId]readPaymentHistory = error "Not yet implemented"
p (Version 1 _) v = Payouts . MS.mapKeys (CreditToAddress . BtcAddr) <$> parseJSON (Object v)
p (Version 1 _) v =let parseKey :: String -> Parser CreditToparseKey k = maybe(fail $ "Key " <> k <> " cannot be parsed as a valid BTC address.")(pure . CreditToAddress)(parseBtcAddr $ T.pack k)in Payouts <$> join (traverseKeys parseKey <$> parseJSON (Object v))
{-# LANGUAGE TemplateHaskell #-}module Aftok.Payments whereimport ClassyPreludeimport Control.Lens (makeLenses)import Data.Thyme.Clock as Cimport Data.UUIDimport qualified Network.Bippy.Proto as Pnewtype PaymentRequestId = PaymentRequestId UUID deriving (Show, Eq)newtype PaymentId = PaymentId UUID deriving (Show, Eq)data PaymentRequest (p :: *) (b :: *) = PaymentRequest{ _project :: p, _paymentRequest :: P.PaymentRequest, _paymentRequestDate :: C.UTCTime, _billable :: b}makeLenses ''PaymentRequestdata Payment r u = Payment{ _request :: r, _payment :: P.Payment, _paymentDate :: C.UTCTime, _payor :: u}makeLenses ''Payment
module Aftok.Time whereimport ClassyPreludenewtype Days = Days Int
traverseKeys :: (Ord k, Applicative f) => (a -> f k) -> Map a b -> f (Map k b)traverseKeys f m =let insf a b m' = flip insert b <$> f a <*> m'in foldrWithKey insf (pure M.empty) m
parseBtcAddr = Just . BtcAddr -- FIXME: perform validation
parseBtcAddr addr = BtcAddr <$> (base58ToAddr . encodeUtf8) addrinstance FromJSON BtcAddr whereparseJSON v = dot <- parseJSON vmaybe (fail $ show t <> " is not a valid BTC address") pure $ parseBtcAddr t
Description: Create tables for persistence of billable & payments data.Created: 2016-12-31 03:45:38.125915 UTCDepends: 2016-10-13_05-36-55_user-event-logApply: |create type aftok_event_t as enum('create_user','create_project','add_user_to_project','create_invitation','accept_invitation','create_event','amend_event','create_auction','create_bid','create_billable','create_payment_request','create_payment');-- a log of raw events - the current state of the database-- should be reproducible by replaying the entire history of-- eventscreate table if not exists aftok_events (id uuid primary key default uuid_generate_v4(),event_time timestamp with time zone not null,event_type aftok_event_t not null,event_json json not null);create type recurrence_t as enum ('onetime', 'weekly', 'semimonthly', 'monthly', 'annually');create table if not exists billables (id uuid primary key default uuid_generate_v4(),project_id uuid not null references projects(id),event_id uuid not null references aftok_events(id),name text not null,description text,recurrence_type recurrence_t not null,recurrence_count int,billing_amount numeric not null,grace_period_days int not null);create table if not exists subscriptions (id uuid primary key default uuid_generate_v4(),user_id uuid not null references users(id),billable_id uuid not null references billables(id),event_id uuid not null references aftok_events(id));create table if not exists payment_requests (id uuid primary key default uuid_generate_v4(),subscription_id uuid not null references subscriptions(id),event_id uuid not null references aftok_events(id),request_data bytea not null);create table if not exists payments (id uuid primary key default uuid_generate_v4(),payment_request_id uuid not null references payment_requests(id),event_id uuid not null references aftok_events(id),payment_data bytea not null);Revert: |drop table payments;drop table payment_requests;drop table subscriptions;drop table billables;drop table aftok_events;
module Aftok.Snaplet.Payments whereimport ClassyPreludeimport Network.Bippyimport Network.Bippy.Typesimport Snap.Coreimport Snap.SnapletrequestPaymentHandler :: Handler App ApprequestPaymentHandler = do-- get payout percentages from payouts handleruid <- requireUserIdpid <- requireProjectIdptime <- liftIO $ C.getCurrentTimepayouts <- snapEval $ fc (ReadWorkIndex pid)pure $ payouts (toDepF $ project ^. depf) ptime widx-- look up the outstandingundefined
let parseUser = User <$> (UserName <$> v .: "username")<*> (BtcAddr <$> v .: "btcAddr")<*> (Email <$> v .: "email")
let parseUser = User <$> (UserName <$> v .: "username")<*> (parseBtcAddr <$> v .: "btcAddr")<*> (Email <$> v .: "email")
extra-deps:- snaplet-postgresql-simple-0.6.0.4
- location:git: git@github.com:aftok/bippy.gitcommit: 6284d5fff3954e0e52d559298364035a220867afextra-dep: trueallow-newer: trueextra-deps:- aeson-0.11.2.1- base-orphans-0.5.4- bytestring-builder-0.10.8.1.0- call-stack-0.1.0- haskoin-core-0.4.0- hspec-2.3.2- hspec-core-2.3.2- hspec-discover-2.3.2- hspec-expectations-0.8.2- mmorph-1.0.9- mono-traversable-0.10.2- murmur3-1.0.1- pbkdf-1.1.1.1- postgresql-libpq-0.9.2.0- postgresql-simple-0.5.2.1