Initial work on payments API
[?]
Dec 21, 2016, 5:23 AM
DFOBMSAODB3NKW37B272ZXA2ML5HMIH3N3C4GT2DPEQS7ZFK4SNACDependencies
- [2]
NAS4BFL4Trivial stylish-haskell reformat. - [3]
M4KM76DGMerge branch 'stackify' - [4]
EZQG2APBUpdate task list. - [5]
2XQD6KKKAdd invitation logic and clean up DBProg error handling. - [6]
BXGLKYRXAdded primitive user registration handler. - [7]
RN7EI6INUpdate database layer to use CreditTo - [8]
FXJQACESEnsure that auction is not ended at the time of bid - [9]
64C6AWH6Rename Ananke -> Quixotic, project reboot. - [10]
RB2ETNIFAdd skeletal PureScript client project. - [11]
POX3UAMTEnabling logging of time to contributor/project accounts - [12]
A6HKMINBAttempting to improve JSON handling. - [13]
2Y2QZFVFSwitch to more modern cabal2nix-based workflow. - [14]
EKY7U7SKFinish conversion to stack. - [15]
NVOCQVASInitial failing tests. - [16]
DLZRD7VBAdd a preliminary, probably somewhat broken set of setup instructions. - [17]
KEP5WUFJConvert project to stack-based build. - [18]
T44T2PDLRename trust.txt to README.md - [19]
E2KOBKIJAdd setup script detailing the setup of the docker host. - [20]
5W5M56VJMove library code to 'lib' - [21]
O5FVTOM6Undo JSON silliness, enable a couple more routes. - [22]
OBFPJS2GProject successfully builds and tests under nix. - [23]
Z3MK2PJ5Add GET handler for retrieving auction data. - [24]
EQXRXRZDChanged to use tasty instead of test-framework - [25]
Y35QCWYWMinor improvement in WorkIndex type to eliminate duplicated information. - [26]
45AI46JNMove readme to inception.md - [27]
373LXH2XAdd MAYBE.md, update task list. - [28]
HALRDT2FAdded initial auction create route. - [29]
IZEVQF62Work in progress replacing sqlite with postgres. - [30]
RSEB2NFGReplacing Snap with Scotty. - [31]
4ZLEDBK7Initial attempts at dockerizing, cabal isn't cooperating. - [32]
ADMKQQGCInitial empty Snap project. - [33]
NTPC7KJETrivial changes, feature scratchpad. - [34]
LAROLAYUWIP - [35]
PBD7LZYQPostgres & auth are beginning to function. - [36]
NEDDHXUKReformat via stylish-haskell - [37]
LEINLS3XUpdate deployment documentation. - [38]
IRG4KNAETrivial deletion. - [39]
TNR3TEHKSwitch to Postgres + snaplet arch compiles. - [40]
5OI44E4EAdd authentication to auction search. - [41]
4U7F3CPITHE GREAT RENAMING OF THINGS! - [42]
WO2MINIFAuctions now compile! - [43]
EKI57EJRAdd alternative implementation of auction winner determination. - [44]
75N3UJ4JMore progression toward lenses. - [45]
EPOYLP7OA little .gitignore cleanup. - [46]
AXKKXBWNInitial attempt at writing down my ideas for a company based on trust. - [47]
2WOOGXDHUse dbmigrations to manage database state. - [48]
7DBNV3GVInitial, stack-based impl of time log event reduction. - [49]
7VGYLTMUClean up schema version handling. - [50]
45QJYWN3Fixing up the README. Still struggling with the ending. - [51]
ZP62WC47Begin conversion to build with stack. - [52]
QMRKFEPGRefactor QDB to use a free monad algebra instead. - [53]
QO4NFWIYAdded sample config file. - [54]
3QVT6MA6Add database support for event amend operations. - [55]
Z7KS5XHHVery WIP. Wow. - [56]
ASF3UPJLAdd auction creation and bid handlers - [57]
GCVQD44VCreate amends endpoint, switch to UUID primary keys - [*]
BROSTG5KBeginning of modularization of server. - [*]
WZUHEZSBStart of migration back toward snap. - [*]
NLZ3JXLOFix formatting with stylish-haskell. - [*]
W35DDBFYFactor common JSON conversions up into client lib module. - [*]
BWN72T44Don't accept work timestamp from an external source.
Change contents
- file deletion: .ghci
:set -isrc:set -hide-package MonadCatchIO-mtl:set -hide-package monads-fd:set -XOverloadedStrings - replacement in TASKS.md at line 28
- Associate events with user identifier as well as BTC address, and addlate resolution of BTC addresses at point of payout calculation.- add late resolution of BTC addresses at point of payout calculation. - edit in aftok.cabal at line 22
, KindSignatures - edit in aftok.cabal at line 25
Aftok.Billables - edit in aftok.cabal at line 31
Aftok.Payments - edit in aftok.cabal at line 33
Aftok.Time - edit in aftok.cabal at line 40
, bippy == 0.1.0.0 - replacement in aftok.cabal at line 42
, aeson == 0.9.*, aeson >= 0.11.2 - edit in aftok.cabal at line 54
, haskoin-core >= 0.4 - replacement in aftok.cabal at line 62
, postgresql-simple == 0.5.*, postgresql-simple >= 0.5.2.0 - edit in aftok.cabal at line 84
, KindSignatures - edit in aftok.cabal at line 113
, KindSignatures - edit in aftok.cabal at line 116
other-modules: Aftok.QConfig, Aftok.Snaplet, Aftok.Snaplet.Auctions, Aftok.Snaplet.Auth, Aftok.Snaplet.Projects, Aftok.Snaplet.Users, Aftok.Snaplet.Util, Aftok.Snaplet.WorkLog - edit in aftok.cabal at line 133
, bippy - file addition: Billables.hs[3.679]
{-# 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 - replacement in lib/Aftok/Database/PostgreSQL.hs at line 17
import Database.PostgreSQL.Simple.FromFieldimport Database.PostgreSQL.Simple.FromField - edit in lib/Aftok/Database/PostgreSQL.hs at line 20
import Network.Haskoin.Crypto (addrToBase58) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 56
btcAddrParser f v = BtcAddr <$> fromField f vbtcAddrParser 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 - replacement in lib/Aftok/Database/PostgreSQL.hs at line 80
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"} - replacement in lib/Aftok/Database/PostgreSQL.hs at line 88
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"} - replacement in lib/Aftok/Database/PostgreSQL.hs at line 149
<*> fieldWith btcAddrParser<*> fieldWith (optionalField btcAddrParser) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 207
( pid, uid, creditToName c, addr ^. _BtcAddr, eventName e, fromThyme $ e ^. eventTime, m)( pid, uid, creditToName c, addr ^. _BtcAddr . to addrToBase58, eventName e, fromThyme $ e ^. eventTime, m) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 263
( eid, fromThyme $ mt ^. _ModTime, creditToName c, addr ^. _BtcAddr )( eid, fromThyme $ mt ^. _ModTime, creditToName c, addr ^. _BtcAddr . to addrToBase58 ) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 298
, auc ^. (raiseAmount.to fromSatoshi), auc ^. (raiseAmount . satoshi) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 314
, bid ^. (bidAmount.to fromSatoshi), bid ^. (bidAmount . satoshi) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 324
pinsert UserIdlet addrMay :: Maybe ByteStringaddrMay = user' ^? (userAddress . traverse . _BtcAddr . to addrToBase58)in pinsert UserId - replacement in lib/Aftok/Database/PostgreSQL.hs at line 328
(user' ^. (username._UserName), user' ^. (userAddress._BtcAddr), user' ^. userEmail._Email)( user' ^. (username._UserName), addrMay, user' ^. userEmail._Email) - edit in lib/Aftok/Database/PostgreSQL.hs at line 391
dbEval (CreateBillable _) = error "Not implemented"dbEval (ReadBillable _) = error "Not implemented"dbEval (CreatePaymentRequest _ _) = error "Not implemented"dbEval (CreatePayment _ ) = error "Not implemented" - edit in lib/Aftok/Database/PostgreSQL.hs at line 399[3.4454]
- edit in lib/Aftok/Database.hs at line 14
import Aftok.Billables as B - edit in lib/Aftok/Database.hs at line 16
import Aftok.Payments - edit in lib/Aftok/Database.hs at line 19
import Aftok.Types - edit in lib/Aftok/Database.hs at line 53
CreateBillable :: Billable ProjectId Satoshi -> DBOp BillableIdReadBillable :: BillableId -> DBOp (Maybe (Billable ProjectId Satoshi))CreatePaymentRequest :: UserId -> PaymentRequest ProjectId BillableId -> DBOp PaymentRequestIdCreatePayment :: Payment PaymentRequestId UserId -> DBOp PaymentId - edit in lib/Aftok/Database.hs at line 172
-- Billing ops - edit in lib/Aftok/Database.hs at line 175
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" - replacement in lib/Aftok/Database.hs at line 211
createBid :: AuctionId -> UserId -> Bid -> DBProg (BidId)createBid :: AuctionId -> UserId -> Bid -> DBProg BidId - edit in lib/Aftok/Json.hs at line 18[61.1270][63.3]
import qualified Data.Text as T - edit in lib/Aftok/Json.hs at line 29
import Aftok.Util (traverseKeys) - replacement in lib/Aftok/Json.hs at line 121
, "raiseAmount" .= (x ^. (raiseAmount._Satoshi)), "raiseAmount" .= (x ^. (raiseAmount . satoshi)) - replacement in lib/Aftok/Json.hs at line 179
parseCreditToV1 x = CreditToAddress <$> (parseBtcAddrJson =<< (x .: "btcAddr"))parseCreditToV1 x = CreditToAddress <$> (parseJSON =<< (x .: "btcAddr")) - replacement in lib/Aftok/Json.hs at line 184
fmap CreditToAddress . parseBtcAddrJson <$> O.lookup "creditToAddress" o'fmap CreditToAddress . parseJSON <$> O.lookup "creditToAddress" o' - replacement in lib/Aftok/Json.hs at line 200
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)) - replacement in lib/Aftok/Json.hs at line 211
p ver x = badVersion "Payouts" ver xp ver x =badVersion "Payouts" ver x - edit in lib/Aftok/Json.hs at line 238[2.1397]→[3.2983:2984](∅→∅),[3.4673]→[3.2983:2984](∅→∅),[3.2983]→[3.2983:2984](∅→∅),[3.3069]→[3.3069:3235](∅→∅)
parseBtcAddrJson :: Value -> Parser BtcAddrparseBtcAddrJson v = dot <- parseJSON vmaybe (fail $ show t <> " is not a valid BTC address") pure $ parseBtcAddr t - file addition: Payments.hs[3.679]
{-# 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 - edit in lib/Aftok/Project.hs at line 8
- edit in lib/Aftok/Project.hs at line 12
- file addition: Time.hs[3.679]
module Aftok.Time whereimport ClassyPreludenewtype Days = Days Int - replacement in lib/Aftok/Types.hs at line 4
module Aftok.Types wheremodule Aftok.Types (Satoshi(..), satoshi) where - edit in lib/Aftok/Types.hs at line 8
import Network.Bippy.Types (Satoshi(..)) - replacement in lib/Aftok/Types.hs at line 10
newtype Satoshi = Satoshi { fromSatoshi :: Word64 }deriving (Show, Eq, Ord, Num, Real, Bounded)makePrisms ''Satoshisatoshi :: Lens' Satoshi Word64satoshi inj (Satoshi value) = Satoshi <$> inj value - edit in lib/Aftok/Types.hs at line 13
- edit in lib/Aftok/Util.hs at line 8
import Data.Map.Strict as M - edit in lib/Aftok/Util.hs at line 20[3.10494]
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 - replacement in lib/Aftok.hs at line 15
newtype BtcAddr = BtcAddr Text deriving (Show, Eq, Ord)import Network.Haskoin.Crypto (Address(..), base58ToAddr)newtype BtcAddr = BtcAddr Address deriving (Show, Eq, Ord) - replacement in lib/Aftok.hs at line 21
parseBtcAddr = Just . BtcAddr -- FIXME: perform validationparseBtcAddr addr = BtcAddr <$> (base58ToAddr . encodeUtf8) addrinstance FromJSON BtcAddr whereparseJSON v = dot <- parseJSON vmaybe (fail $ show t <> " is not a valid BTC address") pure $ parseBtcAddr t - replacement in lib/Aftok.hs at line 45
, _userAddress :: BtcAddr, _userAddress :: Maybe BtcAddr - replacement in migrations/2016-10-13_05-36-55_user-event-log.txt at line 5
create extension if not exists "uuid-ossp";--create extension if not exists "uuid-ossp"; - file addition: 2016-12-31_03-45-17_create-payments.txt[3.1]
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; - file addition: Payments.hs[3.2082]
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 - replacement in server/Aftok/Snaplet/Users.hs at line 34
let parseUser = User <$> (UserName <$> v .: "username")<*> (BtcAddr <$> v .: "btcAddr")<*> (Email <$> v .: "email")let parseUser = User <$> (UserName <$> v .: "username")<*> (parseBtcAddr <$> v .: "btcAddr")<*> (Email <$> v .: "email") - replacement in stack.yaml at line 4
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 - edit in stack.yaml at line 26
- secp256k1-0.4.5- snaplet-postgresql-simple-0.6.0.4- unix-compat-0.4.3.1- vector-th-unbox-0.2.1.6 - edit in stack.yaml at line 31
#allow-newer: true