Preliminary work to add support for Zcash payments.
[?]
Dec 30, 2020, 8:53 PM
M4PWY5RUV72AEDCNC4O7UKBPHBIACR4354YTSC3SUZGWFV5UBJBQCDependencies
- [2]
Z7CQXTU7Update login scripts, add script for XHR login interface. - [3]
ONSJNBNFUse the more useful `workIndex` endpoint for list_intervals script. - [4]
X3ES7NUAFine. I'll use ormolu. At least it doesn't break the code. - [5]
BROSTG5KBeginning of modularization of server. - [6]
VRMUVBP6Make log script work on OSX. - [7]
QADKFHARAdds CreatePayment handler implementation. - [8]
KEP5WUFJConvert project to stack-based build. - [9]
4U7F3CPITHE GREAT RENAMING OF THINGS! - [10]
RSF6UAJKBreak out api module for timeline. - [11]
LTSVBVA2Update to a recent haskoin-core. Fix Stack build. - [12]
5DRIWGLUImproving TimeLog specs - [13]
OV5AKJHARemove unused LogInterval type. - [14]
TZQJVHBAAdd auction functions to ADB. - [15]
O722AOKEAdd route to allow crediting of events to users/projects. - [16]
73NDXDEZBegin implementation of billing event persistence. - [17]
JFOEOFGAstylish-haskell formatting. - [18]
XTBSG4C7Adding serveJSON combinator to eliminate some boilerplate from handlers. - [19]
QMEYU4MWAdd display for prior intervals. - [20]
HMDM3B55Implement core of payments/billing infrastructure. - [21]
NAS4BFL4Trivial stylish-haskell reformat. - [22]
I2KHGVD4Require project permissions for access to most data. - [23]
RPAJLHMTChange to use UUIDs instead of ints for primary keys. - [24]
NJNMO72SAdd zcash.com submodule and update client to modern halogen. - [25]
AWWC6P5ZAdd migration to include payment network with addresses. - [26]
E7GQXOIDAllow the use of a local .env file to store username/project ID for UI scripts. - [27]
7KZP4RHZSwitch from Data.Time to Data.Thyme - [28]
JV3UEPNCFix Aeson constructors. - [29]
5XFJNUAZStart of addition of project infrastructure. - [30]
BXGLKYRXAdded primitive user registration handler. - [31]
EZQG2APBUpdate task list. - [32]
A6HKMINBAttempting to improve JSON handling. - [33]
GKGVYBZGAdded JSON serialization to TimeLog - [34]
Y35QCWYWMinor improvement in WorkIndex type to eliminate duplicated information. - [35]
WAIX6AGNAdd event serialization for PaymentRequest & Payment - [36]
4FDQGIXNMake payment request retrieval key an opaque 32-bit hash. - [37]
OBFPJS2GProject successfully builds and tests under nix. - [38]
Z24SZOGZReturn richer information from event logging calls. - [39]
LHJ2HFXVAdd property test for auction algorithm. - [40]
ZKFETYRKPrint network information in address parse failure message. - [41]
UWMGUJOWAutoformat sources. - [42]
4R7XIYK3Switch from ClassyPrelude to Relude - [43]
WO2MINIFAuctions now compile! - [44]
M4KM76DGMerge branch 'stackify' - [45]
POX3UAMTEnabling logging of time to contributor/project accounts - [46]
PBD7LZYQPostgres & auth are beginning to function. - [47]
ASF3UPJLAdd auction creation and bid handlers - [48]
2J37EVJMCheck for an open interval on project switch. - [49]
SQ7UMLN5Get z-addr checks working. - [50]
Y3LIJ5USAdd handler for CreatePaymentRequest - [51]
QMRKFEPGRefactor QDB to use a free monad algebra instead. - [52]
SOIAMXLWBuild versioned docker images. - [53]
Z3MK2PJ5Add GET handler for retrieving auction data. - [54]
2G3GNDDUEvent logging is now functioning in postgres. - [55]
LLKTCDRDMinor reorg of aftok.com paths. - [56]
ZIG57EE6Fix project selection, end log end on project switch. - [57]
G4BS4NNDAdd simple shell script demonstrating how to invite a companion. - [58]
ZP62WC47Begin conversion to build with stack. - [59]
GKLIPHR5Fix error in parsing of event metadata - [60]
LAROLAYUWIP - [61]
HALRDT2FAdded initial auction create route. - [62]
GCVQD44VCreate amends endpoint, switch to UUID primary keys - [63]
RN7EI6INUpdate database layer to use CreditTo - [64]
SEWTRB6SImplement payment request creation functions. - [65]
AL37SVTCImplement payments service endpoints. - [66]
4QX5E5ACInitial compilation of payouts function succeeds. - [67]
BSIUHCGFAdd payment response handler. - [68]
5OI44E4EAdd authentication to auction search. - [69]
7DBNV3GVInitial, stack-based impl of time log event reduction. - [70]
U256ZALIAdd captcha check to register route. - [71]
SFWL5626Initial release of UI. - [72]
NEDDHXUKReformat via stylish-haskell - [73]
EW2XN7KUUpdate docker build, clean up migration for payments tables. - [74]
EFSXYZPOAutoformat everything with brittany. - [75]
2MNO5FUYUpgrade LTS version - [76]
UILI6PILThe route-based logStart/logStop is nicer. - [77]
3QVT6MA6Add database support for event amend operations. - [78]
EMVTF2IWWIP moving back to snap. - [79]
B6HWAPDPModularize & update to recent haskoin. - [80]
MB5SHULBAdd route for accepting an invitation with an existing account - [81]
7VGYLTMUClean up schema version handling. - [82]
J6S23MDGUse server timestamps for interval start and end. - [83]
3GBSDS5PFix out-of-date test code, add skeleton for payments spec. - [84]
ENNZIQJGUse live signup API for client. - [85]
QU5FW67RAdd project selection to time tracker. - [86]
4SCFOJGNSpecs for recovering intervals from the log now pass. - [87]
5ZSKPQ3KAdd created_at and auction_start timestamps to auction - [88]
NVOCQVASInitial failing tests. - [89]
TLQ72DSJLenses, sqlite-simple - [90]
IZEVQF62Work in progress replacing sqlite with postgres. - [91]
7HPY3QPFFix linting errors. (yay hlint!) - [92]
HYV3VQADFix a couple of stupid typos. - [93]
BWN72T44Don't accept work timestamp from an external source. - [94]
NLZ3JXLOFix formatting with stylish-haskell. - [95]
LD4GLVSFMore database stuff. - [96]
XZLSHL4DThe server is now (tenuously) running, and serving pages via SSL! - [97]
UOG5H2TWDefault work logging credit to logged-in user. - [98]
FD7SV5I6Fix handling of event_t columns. - [99]
SCXG6TJWMake log reduction safer in presence of overlapping events. - [100]
2KZPOGRBOnce you get Haskell to compile, the tests pass! - [101]
ZTPDQKLAAdd changes to event_credit_to_amendments - [102]
O2BZOX7MAdd signup form, captcha check. - [103]
CDHZL3RPAdd a couple of other CLI utilities for interacing with the service. - [104]
EKY7U7SKFinish conversion to stack. - [105]
O5FVTOM6Undo JSON silliness, enable a couple more routes. - [106]
XJ4EYMIHLet curl prompt for http password, rather than bash. - [107]
64C6AWH6Rename Ananke -> Quixotic, project reboot. - [108]
Q5X5RYQLstylish-haskell reformatting - [109]
2XQD6KKKAdd invitation logic and clean up DBProg error handling. - [110]
KNSI575VCleanup of EventLog types. - [111]
P6NR2CGXBeginning of implementation of depreciation. - [112]
RSEB2NFGReplacing Snap with Scotty. - [113]
O227CEAVAdds storage of original event JSON for some DBOp constructors. - [114]
WZFQDWW4Add retrieval/storage of current exchange rate data to payment recording. - [115]
IPG33FAWAdd billing daemon - [116]
SLL7262CMake depreciation functions more flexible. - [117]
SPJCFHXWUpdate shell scripts to point to https://aftok.com and prompt for input. - [118]
4IQVQL4TAdded client for payouts endpoint. - [119]
3MERL4JAFix incorrect variable in invitation script. - [120]
EKI57EJRAdd alternative implementation of auction winner determination. - [121]
TNR3TEHKSwitch to Postgres + snaplet arch compiles. - [122]
W35DDBFYFactor common JSON conversions up into client lib module. - [123]
FXJQACESEnsure that auction is not ended at the time of bid - [124]
NSRSSSTRUpdate nginx.conf, make aftok host configurable for cli scripts. - [125]
5IDB3IWSIntegrate zcashd-based zaddr validation. - [126]
F4ONFXF4Fix signup database issues. - [127]
DFOBMSAOInitial work on payments API - [128]
NMWWP4ZNTrying out Hspec - [129]
Z7KS5XHHVery WIP. Wow. - [*]
LEINLS3XUpdate deployment documentation. - [*]
2WOOGXDHUse dbmigrations to manage database state. - [*]
ADMKQQGCInitial empty Snap project.
Change contents
- replacement in Makefile at line 4
find lib test server daemon -name \*.hs -exec brittany --write-mode=inplace {} \;ormolu --mode inplace $(find lib server daemon test -name '*.hs') - edit in aftok.cabal at line 38
Aftok.Currency.Bitcoin.PaymentsAftok.Currency.Bitcoin.Bip70 - edit in aftok.cabal at line 41
Aftok.Currency.Zcash.TypesAftok.Currency.Zcash.PaymentsAftok.Currency.Zcash.Zip321 - edit in aftok.cabal at line 46
Aftok.Database.PostgreSQL.Json - edit in aftok.cabal at line 48
Aftok.Database.PostgreSQL.AuctionsAftok.Database.PostgreSQL.BillingAftok.Database.PostgreSQL.EventsAftok.Database.PostgreSQL.ProjectsAftok.Database.PostgreSQL.Users - edit in aftok.cabal at line 57
Aftok.Payments.BitcoinAftok.Payments.ZcashAftok.Payments.Util - edit in aftok.cabal at line 67
, basement - replacement in aftok.cabal at line 72
, base64-bytestring, base64 - edit in aftok.cabal at line 93
, MonadRandom - edit in aftok.cabal at line 102
, scientific - replacement in aftok.cabal at line 109
, thyme, thyme - edit in aftok.cabal at line 113
, uri-encode - edit in daemon/AftokD/AftokM.hs at line 5
{-# LANGUAGE TupleSections #-} - edit in daemon/AftokD/AftokM.hs at line 9
import qualified Aftok.Billing as B - edit in daemon/AftokD/AftokM.hs at line 15
billable, - edit in daemon/AftokD/AftokM.hs at line 17
name, - replacement in daemon/AftokD/AftokM.hs at line 22
import Aftok.Currency.Bitcoin (satoshi)import Aftok.Currency.Bitcoin (Satoshi, _Satoshi)import qualified Aftok.Currency.Bitcoin.Payments as Bitcoinimport Aftok.Currency.Zcash (Zatoshi (..)) - replacement in daemon/AftokD/AftokM.hs at line 28
import Aftok.Payments.Types( PaymentKey (..),paymentKey,paymentRequestTotal,subscription,)import Aftok.Payments.Bitcoin (BillingOps (..), PaymentsConfig)import qualified Aftok.Payments.Types as Pimport qualified Aftok.Payments.Zcash as Zcash - replacement in daemon/AftokD/AftokM.hs at line 42
import Bippy.Types (Satoshi)import Control.Error.Util (maybeT)import Control.Error.Util (exceptT, maybeT) - replacement in daemon/AftokD/AftokM.hs at line 44
( (^.),( (.~),Iso',(^.),from,iso, - edit in daemon/AftokD/AftokM.hs at line 51
over,set, - replacement in daemon/AftokD/AftokM.hs at line 92[5.3207]→[5.3207:3297](∅→∅),[5.3297]→[4.1995:2033](∅→∅),[4.2033]→[5.3335:3383](∅→∅),[5.4254]→[5.3335:3383](∅→∅),[5.3335]→[5.3335:3383](∅→∅)
instance P.AsPaymentError AftokDErr where_PaymentError = _PaymentErr . P._PaymentError_Overdue = _PaymentErr . P._Overdue_SigningError = _PaymentErr . P._SigningError-- instance P.AsPaymentError AftokDErr where-- _PaymentError = _PaymentErr . P._PaymentError-- _Overdue = _PaymentErr . P._Overdue-- _SigningError = _PaymentErr . P._SigningError - replacement in daemon/AftokD/AftokM.hs at line 101
_pcfg :: !P.PaymentsConfig_pcfg :: !PaymentsConfig - replacement in daemon/AftokD/AftokM.hs at line 106[5.3519]→[5.3519:3564](∅→∅),[5.3564]→[4.2166:2267](∅→∅),[4.2267]→[5.3657:3681](∅→∅),[5.4370]→[5.3657:3681](∅→∅),[5.3657]→[5.3657:3681](∅→∅)
instance P.HasPaymentsConfig AftokMEnv wherenetworkMode = pcfg . P.networkModesigningKey = pcfg . P.signingKeypkiData = pcfg . P.pkiDatapaymentsConfig = pcfg-- instance P.HasPaymentsConfig AftokMEnv where-- networkMode = pcfg . P.networkMode-- signingKey = pcfg . P.signingKey-- pkiData = pcfg . P.pkiData-- paymentsConfig = pcfg - replacement in daemon/AftokD/AftokM.hs at line 139
traverse_ createProjectPaymentRequests projectstraverse_ createProjectSubscriptionPaymentRequests projects - replacement in daemon/AftokD/AftokM.hs at line 141
createProjectPaymentRequests :: ProjectId -> AftokM ()createProjectPaymentRequests pid = docreateProjectSubscriptionPaymentRequests :: ProjectId -> AftokM ()createProjectSubscriptionPaymentRequests pid = do - replacement in daemon/AftokD/AftokM.hs at line 144
let ops = P.BillingOps memoGen (fmap Just . paymentURL) payloadGenbtcCfg <- asks _pcfglet btcOps = BillingOps _memoGen (fmap Just . bip70PaymentURL) _payloadGenzecCfg = Zcash.PaymentsConfig (Zatoshi 100)pcfg' = P.PaymentsConfig btcOps btcCfg zecCfg - edit in daemon/AftokD/AftokM.hs at line 149
subscriptions <- join <$> traverse (DB.findSubscriptions pid) subscribers - replacement in daemon/AftokD/AftokM.hs at line 151
traverse (\uid -> P.createPaymentRequests ops now uid pid) $ subscriberstraverse_ sendPaymentRequestEmail (join requests)fmap join. exceptT (throwError . PaymentErr) pure$ traverse (\s -> fmap (snd s,) <$> P.createSubscriptionPaymentRequests pcfg' now s) subscriptionstraverse_ sendPaymentRequestEmail requests - replacement in daemon/AftokD/AftokM.hs at line 156
sendPaymentRequestEmail :: P.PaymentRequestId -> AftokM ()sendPaymentRequestEmail reqId = do_Compose :: Iso' (f (g a)) (Compose f g a)_Compose = iso Compose getCompose-- | TODO: Currently will only send email for bip70 requestssendPaymentRequestEmail :: (B.Subscription, (P.PaymentRequestId, P.SomePaymentRequestDetail)) -> AftokM ()sendPaymentRequestEmail (sub, (_, P.SomePaymentRequest req)) = do - replacement in daemon/AftokD/AftokM.hs at line 165[4.2423]→[4.2423:2563](∅→∅),[4.2563]→[5.5347:5501](∅→∅),[5.5044]→[5.5347:5501](∅→∅),[5.5347]→[5.5347:5501](∅→∅),[5.5501]→[4.2564:2632](∅→∅),[4.2632]→[5.5569:5614](∅→∅),[5.5118]→[5.5569:5614](∅→∅),[5.5569]→[5.5569:5614](∅→∅),[5.5614]→[4.2633:2829](∅→∅),[5.1179]→[5.5762:5805](∅→∅),[4.2829]→[5.5762:5805](∅→∅),[5.5323]→[5.5762:5805](∅→∅),[5.5762]→[5.5762:5805](∅→∅)
reqMay = dopreq <- DB.findPaymentRequestId reqIdpreq' <- traverseOf P.subscription DB.findSubscriptionBillable preqpreq'' <- traverseOf (P.subscription . customer) DB.findUser preq'traverseOf (P.subscription . billable . project) DB.findProject preq''req <- maybeT (throwError $ DBErr DB.SubjectNotFound) pure reqMaybip70URL <- paymentURL (req ^. paymentKey)mail <- buildPaymentRequestEmail preqCfg req bip70URLlet mailer =maybe(SMTP.sendMailWithLogin _smtpHost)(SMTP.sendMailWithLogin' _smtpHost)_smtpPortliftIO $ mailer _smtpUser _smtpPass mailreq' = over P.billable (\b -> Compose $ sub & B.billable .~ b) reqreq'' <- enrichWithUser req'req''' <- enrichWithProject req''case req''' ^. P.nativeRequest ofP.Bip70Request nreq -> dobip70URL <- bip70PaymentURL (nreq ^. Bitcoin.paymentRequestKey)mail <- buildBip70PaymentRequestEmail preqCfg req''' bip70URLlet mailer =maybe(SMTP.sendMailWithLogin _smtpHost)(SMTP.sendMailWithLogin' _smtpHost)_smtpPortliftIO $ mailer _smtpUser _smtpPass mailP.Zip321Request _ -> pure () - replacement in daemon/AftokD/AftokM.hs at line 180
buildPaymentRequestEmail ::enrichWithUser ::P.PaymentRequest' (Compose (Subscription' UserId) (Billable' p u)) a ->AftokM (P.PaymentRequest' (Compose (Subscription' User) (Billable' p u)) a)enrichWithUser req = dolet sub = req ^. P.billable . from _Composesub' <-maybeT (throwError $ DBErr DB.SubjectNotFound) pure $traverseOf customer DB.findUser subpure (set P.billable (Compose sub') req)enrichWithProject ::P.PaymentRequest' (Compose (Subscription' u) (Billable' ProjectId u')) a ->AftokM (P.PaymentRequest' (Compose (Subscription' u) (Billable' Project u')) a)enrichWithProject req = dolet sub = req ^. P.billable . from _Composesub' <-maybeT (throwError $ DBErr DB.SubjectNotFound) pure $traverseOf (B.billable . project) DB.findProject subpure (set P.billable (Compose sub') req)buildBip70PaymentRequestEmail :: - replacement in daemon/AftokD/AftokM.hs at line 203
P.PaymentRequest' (Subscription' User (Billable' Project UserId Satoshi)) ->P.PaymentRequest' (Compose (Subscription' User) (Billable' Project UserId)) Satoshi -> - replacement in daemon/AftokD/AftokM.hs at line 206[5.1348]→[5.6092:6141](∅→∅),[4.3029]→[5.6092:6141](∅→∅),[5.5584]→[5.6092:6141](∅→∅),[5.6092]→[5.6092:6141](∅→∅)
buildPaymentRequestEmail cfg req paymentUrl = dobuildBip70PaymentRequestEmail cfg req paymentUrl = do - replacement in daemon/AftokD/AftokM.hs at line 210
<$> req^. (subscription . billable . paymentRequestEmailTemplate)<$> (req ^. P.billable . to getCompose . B.billable . paymentRequestEmailTemplate) - replacement in daemon/AftokD/AftokM.hs at line 216
toEmail <- case req ^. (subscription . contactChannel) oftoEmail <- case req ^. (P.billable . to getCompose . contactChannel) of - replacement in daemon/AftokD/AftokM.hs at line 220
pname = req ^. (subscription . billable . project . projectName)total = req ^. (P.paymentRequest . to paymentRequestTotal)pname = req ^. P.billable . to getCompose . B.billable . B.project . projectNametotal = req ^. P.billable . to getCompose . B.billable . B.amount - replacement in daemon/AftokD/AftokM.hs at line 227
("amount_due", show $ total ^. satoshi),("amount_due", show $ total ^. _Satoshi), - replacement in daemon/AftokD/AftokM.hs at line 236[5.7418]→[4.3786:3874](∅→∅),[4.3874]→[5.7531:7639](∅→∅),[5.6447]→[5.7531:7639](∅→∅),[5.7531]→[5.7531:7639](∅→∅)
memoGen ::Subscription' UserId Billable -> C.Day -> C.UTCTime -> AftokM (Maybe Text)memoGen sub billingDate requestTime = doreq <- traverseOf (billable . project) DB.findProjectOrError sub_memoGen ::DB.MonadDB m =>Billable Satoshi ->C.Day ->C.UTCTime ->m (Maybe Text)_memoGen bill billingDate requestTime = doreq <- traverseOf B.project DB.findProjectOrError bill - replacement in daemon/AftokD/AftokM.hs at line 246
<$> (sub ^. (billable . paymentRequestMemoTemplate))<$> (bill ^. paymentRequestMemoTemplate) - replacement in daemon/AftokD/AftokM.hs at line 249
[ ("project_name", req ^. (billable . project . projectName)),("subscription", req ^. (billable . name)),[ ("project_name", req ^. B.project . projectName),("subscription", req ^. B.name), - replacement in daemon/AftokD/AftokM.hs at line 258
paymentURL :: PaymentKey -> AftokM URIpaymentURL (PaymentKey k) = dobip70PaymentURL :: Bitcoin.PaymentKey -> AftokM URIbip70PaymentURL (Bitcoin.PaymentKey k) = do - replacement in daemon/AftokD/AftokM.hs at line 273
payloadGen ::_payloadGen :: - replacement in daemon/AftokD/AftokM.hs at line 275
Subscription' UserId Billable ->Billable Satoshi -> - replacement in daemon/AftokD/AftokM.hs at line 279[5.1018]→[5.8606:8638](∅→∅),[4.4468]→[5.8606:8638](∅→∅),[5.6922]→[5.8606:8638](∅→∅),[5.8606]→[5.8606:8638](∅→∅)
payloadGen _ _ _ = pure Nothing[4.4468]_payloadGen _ _ _ = pure Nothing - replacement in lib/Aftok/Auction.hs at line 6
( satoshi,( _Satoshi, - replacement in lib/Aftok/Auction.hs at line 72
btc bid = toRational $ bid ^. bidAmount . satoshibtc bid = toRational $ bid ^. bidAmount . _Satoshi - replacement in lib/Aftok/Auction.hs at line 88
let winFraction r = r % (bid ^. bidAmount . satoshi)let winFraction r = r % (bid ^. bidAmount . _Satoshi) - replacement in lib/Aftok/Auction.hs at line 114
let winFraction r = r % (bid ^. bidAmount . satoshi)let winFraction r = r % (bid ^. bidAmount . _Satoshi) - edit in lib/Aftok/Billing.hs at line 4
{-# LANGUAGE ExplicitForAll #-} - edit in lib/Aftok/Billing.hs at line 9
import Bippy.Types (Satoshi) - edit in lib/Aftok/Billing.hs at line 14[5.982]→[5.982:1039](∅→∅),[5.1039]→[4.8527:8528](∅→∅),[4.8528]→[5.188:212](∅→∅),[5.1039]→[5.188:212](∅→∅),[5.212]→[5.1040:1041](∅→∅),[5.1040]→[5.1040:1041](∅→∅)
newtype BillableId = BillableId UUID deriving (Show, Eq)makePrisms ''BillableId - replacement in lib/Aftok/Billing.hs at line 52
data Billable' p u c-- | A potentially recurring billable amount.data Billable' p u currency - replacement in lib/Aftok/Billing.hs at line 58
_description :: Text,_description :: Maybe Text,_messageText :: Maybe Text, - replacement in lib/Aftok/Billing.hs at line 61
_amount :: c,_amount :: currency, - replacement in lib/Aftok/Billing.hs at line 63
_requestExpiryPeriod :: Maybe C.NominalDiffTime,_requestExpiryPeriod :: NominalDiffTime, - replacement in lib/Aftok/Billing.hs at line 70
type Billable = Billable' ProjectId UserId Satoshitype Billable amt = Billable' ProjectId UserId amt - replacement in lib/Aftok/Billing.hs at line 72
newtype SubscriptionId = SubscriptionId UUID deriving (Show, Eq)newtype BillableId = BillableId UUID deriving (Show, Eq) - replacement in lib/Aftok/Billing.hs at line 74
makePrisms ''SubscriptionIdmakePrisms ''BillableId - edit in lib/Aftok/Billing.hs at line 79
-- | An association between a customer and a (potentially recurring) billable amount.---- For one-time billing events, the end date should be the same as the start date. - replacement in lib/Aftok/Billing.hs at line 87
_startTime :: C.UTCTime,_endTime :: Maybe C.UTCTime_startTime :: UTCTime,_endTime :: Maybe UTCTime - edit in lib/Aftok/Billing.hs at line 94
newtype SubscriptionId = SubscriptionId UUID deriving (Show, Eq)makePrisms ''SubscriptionId - replacement in lib/Aftok/Billing.hs at line 109
billingSchedule :: forall u. Subscription' u Billable -> [T.Day]billingSchedule :: forall u a. Subscription' u (Billable a) -> [T.Day] - replacement in lib/Aftok/Config.hs at line 5
import Aftok.Currency.Bitcoin (NetworkMode)import Aftok.Payments (PaymentsConfig (..))import Aftok.Currency.Bitcoin (NetworkMode, Satoshi (..))import qualified Aftok.Payments.Bitcoin as Bitcoin - replacement in lib/Aftok/Config.hs at line 44
_exchangeRateServiceURI :: String_exchangeRateServiceURI :: String,_minPayment :: Satoshi - edit in lib/Aftok/Config.hs at line 65
<*> (Satoshi <$> C.lookupDefault 100 cfg "minPayment") - replacement in lib/Aftok/Config.hs at line 76
toPaymentsConfig :: BillingConfig -> IO PaymentsConfigtoPaymentsConfig :: BillingConfig -> IO Bitcoin.PaymentsConfig - replacement in lib/Aftok/Config.hs at line 91
pure $ PaymentsConfig (c ^. networkMode) privKey pkiData[5.13405]pure $ Bitcoin.PaymentsConfig (c ^. networkMode) privKey pkiData (_minPayment c) - file addition: Bitcoin[5.4250]
- file addition: Bip70.hs[0.6659]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Currency.Bitcoin.Bip70( module Bippy.Proto,)whereimport Bippy.Proto - file addition: Payments.hs[0.6659]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Currency.Bitcoin.Payments( PaymentKey (..),_PaymentKey,Payment (..),PaymentRequest (..),amount,txid,address,bip70Payment,paymentKey,bip70Request,paymentRequestKey,)whereimport qualified Bippy.Proto as Bimport Bippy.Types (Satoshi)import Control.Lens (makeLenses, makePrisms)import Haskoin.Address (Address (..))-- A unique identifier for a payment request, suitable-- for URL embedding.newtype PaymentKey = PaymentKey Text deriving (Eq)makePrisms ''PaymentKeydata PaymentRequest= PaymentRequest{ _paymentRequestKey :: PaymentKey,_bip70Request :: B.PaymentRequest}makeLenses ''PaymentRequestdata Payment= Payment{ _amount :: Maybe Satoshi,_txid :: Maybe Text,_address :: Maybe Address,_paymentKey :: PaymentKey,_bip70Payment :: B.Payment}makeLenses ''Payment - replacement in lib/Aftok/Currency/Bitcoin.hs at line 5
module Aftok.Currency.Bitcoin wheremodule Aftok.Currency.Bitcoin( Satoshi (..),_Satoshi,ssub,NetworkMode (..),renderNetworkMode,parseNetworkMode,getNetwork,)where - replacement in lib/Aftok/Currency/Bitcoin.hs at line 21
satoshi :: Lens' Satoshi Word64satoshi inj (Satoshi value) = Satoshi <$> inj value_Satoshi :: Lens' Satoshi Word64_Satoshi inj (Satoshi value) = Satoshi <$> inj value - edit in lib/Aftok/Currency/Bitcoin.hs at line 27
data NetworkId= BTC| BCHderiving (Eq, Show, Ord)renderNetworkId :: NetworkId -> TextrenderNetworkId = \caseBTC -> "btc"BCH -> "bch" - edit in lib/Aftok/Currency/Bitcoin.hs at line 28[5.5010]→[5.5010:5115](∅→∅),[5.5115]→[4.11042:11057](∅→∅),[4.11057]→[5.5130:5131](∅→∅),[5.12595]→[5.5130:5131](∅→∅),[5.5130]→[5.5130:5131](∅→∅)
parseNetworkId :: Text -> Maybe NetworkIdparseNetworkId = \case"btc" -> Just BTC"bch" -> Just BCH_ -> Nothing - edit in lib/Aftok/Currency/Bitcoin.hs at line 31
renderNetworkMode :: NetworkMode -> TextrenderNetworkMode = \caseLiveMode -> "live"TestMode -> "test" - replacement in lib/Aftok/Currency/Bitcoin.hs at line 47[5.5421]→[5.5421:5668](∅→∅),[5.5668]→[4.11097:11117](∅→∅),[4.11117]→[5.5688:5712](∅→∅),[5.12675]→[5.5688:5712](∅→∅),[5.5688]→[5.5688:5712](∅→∅),[5.5712]→[4.11118:11138](∅→∅),[4.11138]→[5.5732:5756](∅→∅),[5.12700]→[5.5732:5756](∅→∅),[5.5732]→[5.5732:5756](∅→∅),[5.5756]→[4.11139:11154](∅→∅)
toNetwork :: NetworkMode -> NetworkId -> NetworktoNetwork LiveMode = \caseBTC -> btcBCH -> bchtoNetwork TestMode = \caseBTC -> btcTestBCH -> bchTesttoNetworkId :: Network -> Maybe NetworkIdtoNetworkId n = case getNetworkName n of"btc" -> Just BTC"btcTest" -> Just BTC"bch" -> Just BCH"bchTest" -> Just BCH_ -> Nothing[5.5421]getNetwork :: NetworkMode -> NetworkgetNetwork = \caseLiveMode -> btcTestMode -> btcTest - file addition: Zcash[5.4250]
- file addition: Payments.hs[0.8302]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Currency.Zcash.Payments whereimport Aftok.Currency.Zcash.Types (Zatoshi)import Control.Lens (makeLenses, makePrisms)newtype TxId = TxId TextmakePrisms ''TxIddata Payment= Payment{ _amount :: Zatoshi,_txid :: TxId}makeLenses ''Payment - file addition: Types.hs[0.8302]
{-# LANGUAGE DerivingVia #-}{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE StandaloneDeriving #-}{-# LANGUAGE TemplateHaskell #-}module Aftok.Currency.Zcash.Types whereimport Control.Lens (makePrisms)coin :: Word64coin = 100000000maxMoney :: Word64maxMoney = 21000000 * coinnewtype IVK = IVK {ivkText :: Text}deriving (Eq, Ord, Show)makePrisms ''IVKnewtype Address = Address {zaddrText :: Text}deriving (Eq, Ord, Show)makePrisms ''Addressnewtype Zatoshi = Zatoshi Word64deriving stock (Eq, Ord, Show)makePrisms ''Zatoshiclass ToZatoshi a wheretoZatoshi :: a -> Maybe Zatoshiinstance ToZatoshi Word64 wheretoZatoshi amt =if amt > maxMoney then Nothing else Just (Zatoshi amt)instance Semigroup Zatoshi where(Zatoshi a) <> (Zatoshi b) = Zatoshi (a + b)data ZAddrType= Sprout| SaplingdecodeAddrType :: Text -> Maybe ZAddrTypedecodeAddrType = \case"sprout" -> Just Sprout"sapling" -> Just Sapling_ -> Nothingnewtype Memo = Memo ByteString - file addition: Zip321.hs[0.8302]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Currency.Zcash.Zip321 whereimport Aftok.Currency.Zcash.Typesimport Control.Lens ((^.), makeLenses, makePrisms)import Data.Attoparsec.Text( Parser,char,choice,decimal,option,parseOnly,scientific,sepBy1,string,takeText,takeTill,takeWhile1,)import Data.ByteString.Base64.URL (decodeBase64, encodeBase64Unpadded)import Data.Char (isAlpha, isAscii, isDigit)import Data.List.NonEmpty (zip)import qualified Data.Map.Strict as Mimport Data.Scientific (toBoundedInteger)import Data.Text (any, intercalate, pack, unpack)import Network.URI.Encode (decodeText, encodeTextWith)import Text.Printf (printf)import Prelude hiding (any, intercalate, zip)data PaymentItem= PaymentItem{ _address :: Address,_amount :: Zatoshi,_memo :: Maybe Memo,_message :: Maybe Text,_label :: Maybe Text,_other :: [(Text, Text)] -- TODO: param name restrictions}makeLenses ''PaymentItemdata PaymentRequest= PaymentRequest{ _items :: NonEmpty PaymentItem}makeLenses ''PaymentRequest-- The set of ASCII characters that are excepted from percent-encoding according-- to the definition of ZIP 321.---- unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"-- allowed-delims = "!" / "$" / "'" / "(" / ")" / "*" / "+" / "," / ";"-- qchar = unreserved / pct-encoded / allowed-delims / ":" / "@"qchar :: Char -> Boolqchar c =(isAscii c && isAlpha c)|| isDigit c|| any (== c) "-._!$'()*+,;:@"paramIndex :: Maybe Int -> TextparamIndex = maybe "" (\i -> pack (printf ".%d" i)) . find (> 0)addrParam :: Maybe Int -> Address -> TextaddrParam i (Address t) = strParam "address" i tamountParam :: Maybe Int -> Zatoshi -> TextamountParam i (Zatoshi value) ="amount" <> paramIndex i <> "=" <> valueTextwherecoins = value `div` coinzats = value `mod` coinvalueText =pack $if zats == 0then printf "%d" coinselse printf "%d.%0.8d" coins zatsstrParam :: Text -> Maybe Int -> Text -> TextstrParam l i value =l <> paramIndex i <> "=" <> encodeTextWith qchar valuememoParam :: Maybe Int -> Memo -> TextmemoParam i (Memo bytes) = "memo" <> paramIndex i <> "=" <> encodeBase64Unpadded bytesitemPartial :: Maybe Int -> PaymentItem -> [Text]itemPartial i item =catMaybes[ Just $ amountParam i (item ^. amount),memoParam i <$> (item ^. memo),strParam "message" i <$> (item ^. message),strParam "label" i <$> (item ^. label)]itemsParams :: NonEmpty PaymentItem -> NonEmpty TextitemsParams xs =intercalate "&" . toList . itemParams <$> zip (Just <$> fromList [1 ..]) xswhereitemParams (i, item) =addrParam i (item ^. address) : itemPartial i itemtoURI :: PaymentRequest -> TexttoURI req =case req ^. items ofi :| [] ->"zcash:" <> zaddrText (i ^. address) <> "?"<> intercalate "&" (itemPartial Nothing i)xs ->"zcash:?" <> intercalate "&" (toList $ itemsParams xs)addrElem :: Char -> BooladdrElem c = isDigit c || (isAscii c && isAlpha c)data Zip321Param= AddrParam Address| AmountParam Zatoshi| MemoParam Memo| LabelParam Text| MessageParam Text| OtherParam Text TextmakePrisms ''Zip321Paramtype IndexedParam = (Int, Zip321Param)zip321Parser :: Parser PaymentRequestzip321Parser = dovoid $ string "zcash:"addr0 <- toAddress <$> takeTill (== '?')params' <- sepBy1 zip321Param (char '&')let params = second (: []) <$> (toList addr0 <> params')grouped = M.fromListWith (<>) paramsgroups <- maybe (fail "Parameter list was empty.") pure (nonEmpty $ M.toAscList grouped)either (fail . unpack) (pure . PaymentRequest) $ traverse (toPaymentItem . snd) groupswheretoAddress addr =if addr == ""then Nothingelse Just (0, AddrParam $ Address addr)zip321Param =choice[ parseAddrParam,parseAmountParam,parseMemoParam,parseLabelParam,parseMessageParam,parseOtherParam]toPaymentItem :: [Zip321Param] -> Either Text PaymentItemtoPaymentItem = error "Not yet implemented." --PaymentItem <$> note "Payment address is required"indexedParam :: Text -> Parser Zip321Param -> Parser IndexedParamindexedParam name valuep = dovoid $ string nameidx <- option 0 (char '.' *> decimal)(,) <$> pure idx <*> (char '=' *> valuep)parseAddrParam :: Parser IndexedParamparseAddrParam = indexedParam "address" (AddrParam . Address <$> takeWhile1 addrElem)parseAmountParam :: Parser IndexedParamparseAmountParam = indexedParam "amount" $ dos <- scientificlet zats = s * fromIntegral coinmaybe(fail "Amount is out of bounds")(pure . AmountParam . Zatoshi)(toBoundedInteger zats)parseMemoParam :: Parser IndexedParamparseMemoParam = indexedParam "memo" $ dot <- takeTexteither(\e -> fail . unpack $ "Base64 decoding of memo value failed: " <> e)(pure . MemoParam . Memo)(decodeBase64 $ encodeUtf8 t)parseLabelParam :: Parser IndexedParamparseLabelParam = indexedParam "label" (LabelParam . decodeText <$> takeText)parseMessageParam :: Parser IndexedParamparseMessageParam = indexedParam "message" (MessageParam . decodeText <$> takeText)parseOtherParam :: Parser IndexedParamparseOtherParam = dopname <- takeWhile1 paramNameCharidx <- option 0 (char '.' *> decimal)void (char '=')value <- decodeText <$> takeTextpure (idx, OtherParam pname value)whereparamNameChar c = isDigit c || (isAscii c && isAlpha c) || c == '+' || c == '-'parseURI :: Text -> Either String PaymentRequestparseURI = parseOnly zip321Parser - replacement in lib/Aftok/Currency/Zcash.hs at line 4
( ZAddr (..),_ZAddr,( Z.Address (..),Z._Address,Z.IVK (..),Z._IVK, - replacement in lib/Aftok/Currency/Zcash.hs at line 11
Zatoshi,ToZatoshi (..),Z.Zatoshi (..),Z._Zatoshi,Z.ToZatoshi (..), - edit in lib/Aftok/Currency/Zcash.hs at line 16
getUserDiversifiedAddress, - edit in lib/Aftok/Currency/Zcash.hs at line 20
import Aftok.Currency.Zcash.Types as Zimport Aftok.Types (UserId) - edit in lib/Aftok/Currency/Zcash.hs at line 23
import Control.Lens (makePrisms) - edit in lib/Aftok/Currency/Zcash.hs at line 44[5.781]→[5.781:860](∅→∅),[5.860]→[5.1491:1492](∅→∅),[5.1491]→[5.1491:1492](∅→∅),[5.1492]→[4.11928:11970](∅→∅),[4.11970]→[5.1536:1563](∅→∅),[5.1536]→[5.1536:1563](∅→∅),[5.1563]→[4.11971:11972](∅→∅),[4.11972]→[5.1563:1582](∅→∅),[5.1563]→[5.1563:1582](∅→∅),[5.1582]→[5.861:922](∅→∅),[5.922]→[4.11973:11974](∅→∅),[4.11974]→[5.922:1002](∅→∅),[5.922]→[5.922:1002](∅→∅),[5.1002]→[5.1582:1583](∅→∅),[5.1582]→[5.1582:1583](∅→∅),[5.1583]→[5.1003:1113](∅→∅),[5.1113]→[5.1583:1622](∅→∅),[5.1583]→[5.1583:1622](∅→∅)
coin :: Word64coin = 100000000maxMoney :: Word64maxMoney = 21000000 * coinnewtype ZAddr = ZAddr {zaddrText :: Text}deriving (Eq, Ord, Show)makePrisms ''ZAddrnewtype Zatoshi = Zatoshi Word64deriving (Eq, Ord, Show)makePrisms ''Zatoshiclass ToZatoshi a wheretoZatoshi :: a -> Maybe Zatoshiinstance ToZatoshi Word64 wheretoZatoshi amt =if amt > maxMoney then Nothing else Just (Zatoshi amt)data ZAddrType= Sprout| Sapling - replacement in lib/Aftok/Currency/Zcash.hs at line 100
vzrAddrType :: Maybe ZAddrTypevzrAddrType :: Maybe Z.ZAddrType - edit in lib/Aftok/Currency/Zcash.hs at line 114[4.13035]→[5.2053:2054](∅→∅),[5.2053]→[5.2053:2054](∅→∅),[5.2054]→[5.2661:2726](∅→∅),[5.2726]→[5.2342:2411](∅→∅),[5.2342]→[5.2342:2411](∅→∅)
decodeAddrType :: Text -> Maybe ZAddrTypedecodeAddrType = \case"sprout" -> Just Sprout"sapling" -> Just Sapling_ -> Nothing - replacement in lib/Aftok/Currency/Zcash.hs at line 115
parseAddrType :: A.Object -> Parser (Maybe ZAddrType)parseAddrType :: A.Object -> Parser (Maybe Z.ZAddrType) - replacement in lib/Aftok/Currency/Zcash.hs at line 118
let typeMay = decodeAddrType <$> typeStrlet typeMay = Z.decodeAddrType <$> typeStr - replacement in lib/Aftok/Currency/Zcash.hs at line 131
rpcValidateZAddr :: Manager -> ZcashdConfig -> Text -> IO (Either (RPCError ZValidateAddressErr) ZAddr)rpcValidateZAddr :: Manager -> ZcashdConfig -> Text -> IO (Either (RPCError ZValidateAddressErr) Z.Address) - replacement in lib/Aftok/Currency/Zcash.hs at line 138
Just Sprout -> Left (RPCError SproutAddress)Just Sapling -> Right (ZAddr addr)Just Z.Sprout -> Left (RPCError SproutAddress)Just Z.Sapling -> Right (Z.Address addr) - replacement in lib/Aftok/Currency/Zcash.hs at line 146
{ addressType :: ZAddrType-- , address :: ZAddr{ addressType :: Z.ZAddrType-- , address :: Z.Address - replacement in lib/Aftok/Currency/Zcash.hs at line 155
-- <*> (ZAddr <$> v .: "address")-- <*> (Z.Address <$> v .: "address") - replacement in lib/Aftok/Currency/Zcash.hs at line 178
Sprout -> Left . RPCError $ SproutViewingKeySapling -> Right ()[5.4828]Z.Sprout -> Left . RPCError $ SproutViewingKeyZ.Sapling -> Right ()getUserDiversifiedAddress :: UserId -> IVK -> AddressgetUserDiversifiedAddress = error "Not Yet Implemented." - edit in lib/Aftok/Currency.hs at line 1
{-# LANGUAGE TemplateHaskell #-}{-# LANGUAGE TypeApplications #-} - edit in lib/Aftok/Currency.hs at line 5
import qualified Aftok.Currency.Zcash as Zcashimport qualified Bippy.Types as Bitcoinimport Control.Lens (view)import qualified Haskoin.Address as Bitcoin - replacement in lib/Aftok/Currency.hs at line 11
import Data.Aeson (Value)import Data.Aeson.Types (Parser)data Currency a c whereBTC :: Currency Bitcoin.Address Bitcoin.SatoshiZEC :: Currency Zcash.Address Zcash.Zatoshidata Currency' c = forall a. Currency' (Currency a c) - replacement in lib/Aftok/Currency.hs at line 17
data Network a= Network{ addressFromJSON :: Parser a,addressToJSON :: a -> Valuedata Amount= forall a c.Amount{ currency :: !(Currency a c),value :: !c - edit in lib/Aftok/Currency.hs at line 23[4.13890]
scaleCurrency :: Currency a c -> c -> Rational -> Maybe cscaleCurrency c amount factor = case c ofBTC -> (\(Bitcoin.Satoshi amt) -> Just $ Bitcoin.Satoshi ((round $ toRational amt * factor) :: Word64)) amountZEC -> (\amt -> Zcash.toZatoshi ((round $ toRational (view Zcash._Zatoshi amt) * factor) :: Word64)) amount - file addition: Auctions.hs[5.6049]
{-# LANGUAGE GADTs #-}{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE LambdaCase #-}{-# LANGUAGE QuasiQuotes #-}module Aftok.Database.PostgreSQL.Auctions( createAuction,findAuction,createBid,findBids,)whereimport Aftok.Auction( Auction (..),AuctionId (..),Bid (..),BidId (..),_AuctionId,auctionEnd,bidAmount,bidSeconds,bidTime,bidUser,initiator,projectId,raiseAmount,)-- import Aftok.Currency ( Amount(..) )-- import qualified Aftok.Currency.Bitcoin as Bitcoinimport Aftok.Currency.Bitcoin (_Satoshi)-- import qualified Aftok.Currency.Zcash as Zcashimport Aftok.Database ()import Aftok.Database.PostgreSQL.Types( DBM,btcAmountParser,idParser,pinsert,pquery,utcParser,)import Aftok.Types( ProjectId (..),UserId (..),_ProjectId,_UserId,)import Control.Lensimport Data.Hourglass (Seconds (..))import qualified Data.Thyme.Time as Cimport Database.PostgreSQL.Simple (Only (..))import Database.PostgreSQL.Simple.FromField ()import Database.PostgreSQL.Simple.FromRow (RowParser, field)import Database.PostgreSQL.Simple.SqlQQ (sql)import Safe (headMay)import Prelude hiding (null)auctionParser :: RowParser AuctionauctionParser =Auction<$> idParser ProjectId<*> idParser UserId<*> utcParser<*> btcAmountParser<*> utcParser<*> utcParserbidParser :: RowParser BidbidParser =Bid <$> idParser UserId <*> (Seconds <$> field) <*> btcAmountParser <*> utcParsercreateAuction :: Auction -> DBM AuctionIdcreateAuction auc =pinsertAuctionId[sql| INSERT INTO auctions (project_id, initiator_id, raise_amount, end_time)VALUES (?, ?, ?, ?) RETURNING id |]( auc ^. (projectId . _ProjectId),auc ^. (initiator . _UserId),auc ^. (raiseAmount . _Satoshi),auc ^. (auctionEnd . to C.fromThyme))findAuction :: AuctionId -> DBM (Maybe Auction)findAuction aucId =headMay<$> pqueryauctionParser[sql| SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_timeFROM auctionsWHERE id = ? |](Only (aucId ^. _AuctionId))createBid :: AuctionId -> Bid -> DBM BidIdcreateBid (AuctionId aucId) bid =pinsertBidId[sql| INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time)VALUES (?, ?, ?, ?, ?) RETURNING id |]( aucId,bid ^. (bidUser . _UserId),case bid ^. bidSeconds of(Seconds i) -> i,bid ^. (bidAmount . _Satoshi),bid ^. (bidTime . to C.fromThyme))findBids :: AuctionId -> DBM [(BidId, Bid)]findBids aucId =pquery((,) <$> idParser BidId <*> bidParser)[sql| SELECT id, bidder_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ? |](Only (aucId ^. _AuctionId)) - file addition: Billing.hs[5.6049]
{-# LANGUAGE QuasiQuotes #-}{-# LANGUAGE TypeApplications #-}module Aftok.Database.PostgreSQL.Billing( createBillable,findBillable,findBillables,createSubscription,findSubscription,findSubscriptions,findSubscribers,storePaymentRequest,findPaymentRequestByKey,findPaymentRequestById,findSubscriptionPaymentRequests,findSubscriptionUnpaidRequests,createPayment,findPayments,)whereimport Aftok.Billing( Billable,Billable' (..),BillableId (..),ContactChannel (..),Recurrence (..),Subscription,Subscription' (..),SubscriptionId (..),_BillableId,_SubscriptionId,amount,description,gracePeriod,name,paymentRequestEmailTemplate,paymentRequestMemoTemplate,project,recurrence,recurrenceCount,recurrenceName,)import Aftok.Currency (Amount (..), Currency (..))import Aftok.Currency.Bitcoin (Satoshi)import qualified Aftok.Currency.Bitcoin as Bitcoinimport qualified Aftok.Currency.Bitcoin.Payments as Bitcoinimport Aftok.Currency.Zcash (Zatoshi)import Aftok.Database.PostgreSQL.Json( nativeRequestJSON,parseBip70PaymentRequestJSON,parseBitcoinPaymentJSON,parseZcashPaymentJSON,parseZip321PaymentRequestJSON,paymentJSON,)import Aftok.Database.PostgreSQL.Types( DBM,currencyAmountParser,currencyType,currencyValue,idParser,nominalDiffTimeParser,nullField,pinsert,pquery,)import Aftok.Payments.Types( NativePayment (..),NativeRequest (..),Payment,Payment' (Payment),PaymentId (..),PaymentRequest,PaymentRequest' (..),PaymentRequestId (..),PaymentRequestId,SomePaymentRequest (..),SomePaymentRequestDetail,_PaymentRequestId,billingDate,bip70Request,createdAt,nativeRequest,paymentDate,paymentRequest,)import Aftok.TimeLog( EventId (..),_EventId,)import Aftok.Types( Email (..),ProjectId (..),UserId (..),_ProjectId,_UserId,)import Control.Lens ((.~), (^.), (^?), _Just, to, view)import Data.Aeson (encode)import Data.Aeson.Types (parseEither)import qualified Data.Thyme.Clock as Cimport qualified Data.Thyme.Time as Cimport Database.PostgreSQL.Simple (Only (..), ResultError (Incompatible))import Database.PostgreSQL.Simple.FromField (FieldParser, returnError, typename)import Database.PostgreSQL.Simple.FromRow (RowParser, field, fieldWith)import Database.PostgreSQL.Simple.SqlQQ (sql)import Safe (headMay)import Prelude hiding (null)billableParser :: RowParser (Billable Amount)billableParser =Billable<$> idParser ProjectId<*> idParser UserId<*> field<*> field<*> field<*> recurrenceParser<*> currencyAmountParser<*> field<*> fieldWith nominalDiffTimeParser<*> field<*> fieldrecurrenceParser :: RowParser RecurrencerecurrenceParser = join $ fieldWith recurrenceParser'recurrenceParser' :: FieldParser (RowParser Recurrence)recurrenceParser' f v = dotn <- typename fif tn /= "recurrence_t"then returnError Incompatible f "column was not of type recurrence_t"else maybe empty (pure . parser . decodeUtf8) vwhereparser :: Text -> RowParser Recurrenceparser = \case"annually" -> nullField *> pure Annually"monthly" -> Monthly <$> field--"semimonthly" = nullField *> pure SemiMonthly"weekly" -> Weekly <$> field"onetime" -> nullField *> pure OneTime_ -> emptysubscriptionParser :: RowParser SubscriptionsubscriptionParser =Subscription<$> idParser UserId<*> idParser BillableId<*> (EmailChannel . Email <$> field)<*> (C.toThyme <$> field)<*> ((fmap C.toThyme) <$> field)bip70RequestParser :: RowParser (NativeRequest Satoshi)bip70RequestParser =Bip70Request <$> ((either (const empty) pure . parseEither parseBip70PaymentRequestJSON) =<< field)zip321RequestParser :: RowParser (NativeRequest Zatoshi)zip321RequestParser =Zip321Request <$> ((either (const empty) pure . parseEither parseZip321PaymentRequestJSON) =<< field)paymentRequestDetailParser :: RowParser SomePaymentRequestDetailpaymentRequestDetailParser = dobillable <- billableParserctime :: C.UTCTime <- C.toThyme <$> fieldbillDay :: C.Day <- C.toThyme <$> fieldcase billable ^. amount of(Amount BTC sats) -> donativeReq <- bip70RequestParserpure . SomePaymentRequest $ PaymentRequest (billable & amount .~ sats) ctime billDay nativeReq(Amount ZEC zats) -> donativeReq <- zip321RequestParserpure . SomePaymentRequest $ PaymentRequest (billable & amount .~ zats) ctime billDay nativeReqpaymentParser :: Bitcoin.NetworkMode -> PaymentRequestId -> Currency a c -> RowParser (Payment c)paymentParser nmode prid ccy = dod :: C.UTCTime <- C.toThyme <$> fieldcase ccy ofBTC -> Payment (Const prid) d <$> bitcoinPaymentParser nmodeZEC -> Payment (Const prid) d <$> zcashPaymentParserbitcoinPaymentParser :: Bitcoin.NetworkMode -> RowParser (NativePayment Satoshi)bitcoinPaymentParser nmode = dopvalue <- fieldeither(const empty)(pure . BitcoinPayment)(parseEither (parseBitcoinPaymentJSON nmode) pvalue)zcashPaymentParser :: RowParser (NativePayment Zatoshi)zcashPaymentParser = dopvalue <- fieldeither(const empty)(pure . ZcashPayment)(parseEither parseZcashPaymentJSON pvalue)createBillable :: EventId -> UserId -> Billable Amount -> DBM BillableIdcreateBillable eventId _ b = dopinsertBillableId[sql| INSERT INTO billables( project_id, event_id, name, description, recurrence_type, recurrence_count, billing_currency, billing_amount, grace_period_days, payment_request_email_template, payment_request_memo_template)VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id |]( b ^. (project . _ProjectId),eventId ^. _EventId,b ^. name,b ^. description,b ^. (recurrence . to recurrenceName),b ^. (recurrence . to recurrenceCount),b ^. (amount . to currencyType),b ^. (amount . to currencyValue),b ^. (gracePeriod),b ^. (paymentRequestEmailTemplate),b ^. (paymentRequestMemoTemplate))findBillable :: BillableId -> DBM (Maybe (Billable Amount))findBillable bid =headMay<$> pquerybillableParser[sql| SELECT b.project_id, e.created_by,b.name, b.description, b.message,b.recurrence_type, b.recurrence_count,b.billing_currency, b.billing_amount,b.grace_period_days, b.request_expiry_seconds,b.payment_request_email_template, b.payment_request_memo_templateFROM billables b JOIN aftok_events e ON e.id = b.event_idWHERE b.id = ? |](Only (bid ^. _BillableId))findBillables :: ProjectId -> DBM [(BillableId, Billable Amount)]findBillables pid =pquery((,) <$> idParser BillableId <*> billableParser)[sql| SELECT b.id, b.project_id, e.created_by,b.name, b.description, b.message,b.recurrence_type, b.recurrence_count,b.billing_currency, b.billing_amount,b.grace_period_days, b.request_expiry_seconds,b.payment_request_email_template, b.payment_request_memo_templateFROM billables b JOIN aftok_events e ON e.id = b.event_idWHERE b.project_id = ? |](Only (pid ^. _ProjectId))createSubscription :: EventId -> UserId -> BillableId -> C.Day -> DBM SubscriptionIdcreateSubscription eventId uid bid start_date =pinsertSubscriptionId[sql| INSERT INTO subscriptions(user_id, billable_id, event_id, start_date)VALUES (?, ?, ?, ?) RETURNING id |]( view _UserId uid,view _BillableId bid,view _EventId eventId,C.fromThyme start_date)findSubscription :: SubscriptionId -> DBM (Maybe Subscription)findSubscription sid =headMay<$> pquerysubscriptionParser[sql| SELECT id, billable_id, contact_email, start_date, end_dateFROM subscriptions sWHERE s.id = ? |](Only (sid ^. _SubscriptionId))findSubscriptions :: ProjectId -> UserId -> DBM [(SubscriptionId, Subscription)]findSubscriptions pid uid =pquery((,) <$> idParser SubscriptionId <*> subscriptionParser)[sql| SELECT s.id, user_id, billable_id, contact_email, start_date, end_dateFROM subscriptions sJOIN billables b ON b.id = s.billable_idWHERE s.user_id = ?AND b.project_id = ? |](uid ^. _UserId, pid ^. _ProjectId)findSubscribers :: ProjectId -> DBM [UserId]findSubscribers pid =pquery(idParser UserId)[sql| SELECT s.user_idFROM subscripions sJOIN billables b ON s.billable_id = b.idWHERE b.project_id = ? |](Only (pid ^. _ProjectId))storePaymentRequest ::EventId ->Maybe SubscriptionId ->PaymentRequest c ->DBM PaymentRequestIdstorePaymentRequest eid sid req =pinsertPaymentRequestId[sql| INSERT INTO payment_requests(subscription_id, event_id, request_json, url_key, request_time, billing_date)VALUES (?, ?, ?, ?, ?, ?) RETURNING id |]( (^. _SubscriptionId) <$> sid,eid ^. _EventId,req ^. nativeRequest . to nativeRequestJSON,req ^? nativeRequest . to bip70Request . _Just . Bitcoin.paymentRequestKey . Bitcoin._PaymentKey,req ^. createdAt . to C.fromThyme,req ^. billingDate . to C.fromThyme)findPaymentRequestByKey :: Bitcoin.PaymentKey -> DBM (Maybe (PaymentRequestId, SomePaymentRequestDetail))findPaymentRequestByKey (Bitcoin.PaymentKey k) =headMay<$> pquery((,) <$> idParser PaymentRequestId <*> paymentRequestDetailParser)[sql|SELECT r.id,b.project_id, e.created_by, b.name, b.description, b.recurrence_type,b.recurrence_count, b.billing_currency, b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templater.request_time, r.billing_date, r.request_json,FROM payment_requests rJOIN billables b on b.id = s.billable_idJOIN aftok_events e on e.id = b.event_idWHERE r.url_key = ?|](Only k)findPaymentRequestById :: PaymentRequestId -> DBM (Maybe SomePaymentRequestDetail)findPaymentRequestById (PaymentRequestId prid) =headMay<$> pquerypaymentRequestDetailParser[sql|SELECTb.project_id, e.created_by, b.name, b.description, b.recurrence_type,b.recurrence_count, b.billing_currency, b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templater.request_time, r.billing_date, r.request_json,FROM payment_requests rJOIN billables b on b.id = s.billable_idJOIN aftok_events e on e.id = b.event_idWHERE r.id = ?|](Only prid)findSubscriptionPaymentRequests :: SubscriptionId -> DBM [(PaymentRequestId, SomePaymentRequestDetail)]findSubscriptionPaymentRequests sid =pquery((,) <$> idParser PaymentRequestId <*> paymentRequestDetailParser)[sql|SELECT r.id,b.project_id, e.created_by, b.name, b.description, b.recurrence_type,b.recurrence_count, b.billing_currency, b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templater.request_time, r.billing_date, r.request_json,FROM payment_requests rJOIN billables b on b.id = s.billable_idJOIN aftok_events e on e.id = b.event_idWHERE subscription_id = ?|](Only (sid ^. _SubscriptionId))findSubscriptionUnpaidRequests :: SubscriptionId -> DBM [(PaymentRequestId, SomePaymentRequestDetail)]findSubscriptionUnpaidRequests sid =pquery((,) <$> idParser PaymentRequestId <*> paymentRequestDetailParser)[sql| SELECT r.id,b.project_id, e.created_by, b.name, b.description, b.recurrence_type,b.recurrence_count, b.billing_currency, b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templater.request_time, r.billing_date, r.request_json,FROM payment_requests rJOIN subscriptions s on s.id = r.subscription_idJOIN billables b on b.id = s.billable_idJOIN aftok_events e on e.id = b.event_idWHERE subscription_id = ?AND r.id NOT IN (SELECT payment_request_id FROM payments)|](Only (sid ^. _SubscriptionId))createPayment :: EventId -> Payment c -> DBM PaymentIdcreatePayment eventId p = donmode <- asks fstpinsertPaymentId[sql| INSERT INTO payments(payment_request_id, event_id, payment_data, payment_date)VALUES (?, ?, ?, ?) RETURNING id |]( p ^. (paymentRequest . to getConst . _PaymentRequestId),eventId ^. _EventId,p ^. (to (paymentJSON nmode) . to encode),p ^. (paymentDate . to C.fromThyme))findPayments :: Currency a c -> PaymentRequestId -> DBM [(PaymentId, Payment c)]findPayments ccy rid = donmode <- asks fstpquery((,) <$> idParser PaymentId <*> paymentParser nmode rid ccy)[sql| SELECT id, payment_request_id, payment_date, payment_dataFROM paymentsWHERE payment_request_id = ? |](Only (rid ^. _PaymentRequestId)) - file addition: Events.hs[5.6049]
{-# LANGUAGE GADTs #-}{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE LambdaCase #-}{-# LANGUAGE QuasiQuotes #-}module Aftok.Database.PostgreSQL.Events( storeEvent,storeEvent',createEvent,findEvent,findEvents,amendEvent,readWorkIndex,)whereimport Aftok.Database( DBError (EventStorageFailed),DBOp( CreateBillable,CreatePayment,CreateSubscription,StorePaymentRequest),KeyedLogEntry,)import Aftok.Database.PostgreSQL.Json( nativeRequestJSON,paymentJSON,)import Aftok.Database.PostgreSQL.Types( DBM,creditToName,creditToParser,idParser,pinsert,pquery,utcParser,)import Aftok.Intervalimport Aftok.Json( billableJSON,createSubscriptionJSON,)import Aftok.Payments.Typesimport Aftok.TimeLogimport Aftok.Typesimport Control.Lens ((^.), _Just, preview)import Control.Monad.Trans.Except (throwE)import Data.Aeson( Value,)import Data.Thyme.Clock as Cimport Data.Thyme.Timeimport Database.PostgreSQL.Simpleimport Database.PostgreSQL.Simple.FromFieldimport Database.PostgreSQL.Simple.FromRowimport Database.PostgreSQL.Simple.SqlQQ( sql,)import Safe (headMay)import Prelude hiding (null)eventTypeParser :: FieldParser (C.UTCTime -> LogEvent)eventTypeParser f v = dotn <- typename fif tn /= "event_t"then returnError Incompatible f "column was not of type event_t"elsemaybe(returnError UnexpectedNull f "event type may not be null")( maybe (returnError Incompatible f "unrecognized event type value") pure. nameEvent. decodeUtf8)vlogEntryParser :: RowParser LogEntrylogEntryParser =LogEntry<$> creditToParser<*> (fieldWith eventTypeParser <*> utcParser)<*> fieldkeyedLogEntryParser :: RowParser KeyedLogEntrykeyedLogEntryParser =(,,) <$> idParser ProjectId <*> idParser UserId <*> logEntryParserstoreEvent :: DBOp a -> Maybe (DBM EventId)storeEvent = \case(CreateBillable uid b) ->Just $ storeEventJSON (Just uid) "create_billable" (billableJSON b)(CreateSubscription uid bid t) ->Just $storeEventJSON(Just uid)"create_subscription"(createSubscriptionJSON uid bid t)(StorePaymentRequest req) ->Just $storeEventJSON Nothing "create_payment_request" (nativeRequestJSON (req ^. nativeRequest))(CreatePayment p) ->Just $ donmode <- asks fststoreEventJSON Nothing "create_payment" (paymentJSON nmode p)_ -> NothingstoreEvent' :: DBOp a -> DBM EventIdstoreEvent' = maybe (lift $ throwE EventStorageFailed) id . storeEventtype EventType = TextstoreEventJSON :: Maybe UserId -> EventType -> Value -> DBM EventIdstoreEventJSON uid etype v = dotimestamp <- liftIO C.getCurrentTimepinsertEventId[sql| INSERT INTO aftok_events(event_time, created_by, event_type, event_json)VALUES (?, ?, ?, ?) RETURNING id |](fromThyme timestamp, preview (_Just . _UserId) uid, etype, v)createEvent :: ProjectId -> UserId -> LogEntry -> DBM EventIdcreateEvent (ProjectId pid) (UserId uid) (LogEntry c e m) = case c ofCreditToAccount aid' -> dopinsertEventId[sql| INSERT INTO work_events( project_id, user_id, credit_to_type, credit_to_account,, event_type, event_time, event_metadata )VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |]( pid,uid,creditToName c,aid' ^. _AccountId,eventName e,fromThyme $ e ^. eventTime,m)CreditToProject pid' ->pinsertEventId[sql| INSERT INTO work_events( project_id, user_id, credit_to_type, credit_to_project_id, event_type, event_time, event_metadata )VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |]( pid,uid,creditToName c,pid' ^. _ProjectId,eventName e,fromThyme $ e ^. eventTime,m)CreditToUser uid' ->pinsertEventId[sql| INSERT INTO work_events( project_id, user_id, credit_to_type, credit_to_user_id, event_type, event_time, event_metadata)VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |]( pid,uid,creditToName c,uid' ^. _UserId,eventName e,fromThyme $ e ^. eventTime,m)findEvent :: EventId -> DBM (Maybe KeyedLogEntry)findEvent (EventId eid) = doheadMay<$> pquerykeyedLogEntryParser[sql| SELECT project_id, user_id,credit_to_type, credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadata FROM work_eventsWHERE id = ? |](Only eid)findEvents :: ProjectId -> UserId -> RangeQuery -> Word32 -> DBM [LogEntry]findEvents (ProjectId pid) (UserId uid) rquery limit = docase rquery of(Before e) ->pquerylogEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time,event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time <= ?ORDER BY event_time DESCLIMIT ?|](pid, uid, fromThyme e, limit)(During s e) ->pquerylogEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?AND event_time >= ? AND event_time <= ?ORDER BY event_time DESCLIMIT ?|](pid, uid, fromThyme s, fromThyme e, limit)(After s) ->pquerylogEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time >= ?ORDER BY event_time DESCLIMIT ?|](pid, uid, fromThyme s, limit)(Always) ->pquerylogEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?ORDER BY event_time DESCLIMIT ?|](pid, uid, limit)amendEvent :: EventId -> EventAmendment -> DBM AmendmentIdamendEvent (EventId eid) = \case(TimeChange mt t) ->pinsertAmendmentId[sql| INSERT INTO event_time_amendments(event_id, amended_at, event_time)VALUES (?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, fromThyme t)(CreditToChange mt c@(CreditToAccount acctId)) ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_account)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, acctId ^. _AccountId)(CreditToChange mt c@(CreditToProject pid)) ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_project_id)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, pid ^. _ProjectId)(CreditToChange mt c@(CreditToUser uid)) ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_user_id)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, uid ^. _UserId)(MetadataChange mt v) ->pinsertAmendmentId[sql| INSERT INTO event_metadata_amendments(event_id, amended_at, event_metadata)VALUES (?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, v)readWorkIndex :: ProjectId -> DBM WorkIndexreadWorkIndex (ProjectId pid) = dologEntries <-pquerylogEntryParser[sql| SELECT credit_to_type,credit_to_account, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? |](Only pid)pure $ workIndex logEntries - file addition: Json.hs[5.6049]
{-# LANGUAGE TypeApplications #-}module Aftok.Database.PostgreSQL.Json whereimport Aftok.Currency.Bitcoin (NetworkMode, Satoshi (..), _Satoshi, getNetwork)import qualified Aftok.Currency.Bitcoin.Payments as Bitcoinimport Aftok.Currency.Zcash (Zatoshi (..), _Zatoshi)import qualified Aftok.Currency.Zcash.Payments as Zcashimport qualified Aftok.Currency.Zcash.Zip321 as Zip321import Aftok.Json (idValue, obj, parseBtcAddr, v1)import Aftok.Payments.Types( NativePayment (..),NativeRequest (..),Payment,_PaymentRequestId,nativePayment,paymentDate,paymentRequest,)-- import qualified Bippy.Proto as BPimport Control.Lens ((^.), (^?), _Just, review, to, view)import Data.Aesonimport Data.Aeson.Types (Parser)import qualified Data.ByteString.Base64 as B64import Data.ProtocolBuffers (Decode, Encode, decodeMessage, encodeMessage)import Data.Serialize.Get (runGet)import Data.Serialize.Put (runPut)import Data.Text (unpack)-- import Data.Thyme.Calendar (showGregorian)import Haskoin.Address (addrToText)protoBase64 :: Encode a => a -> TextprotoBase64 = B64.encodeBase64 . runPut . encodeMessagefromBase64Proto :: Decode a => Text -> Either Text afromBase64Proto t = (first toText . runGet decodeMessage) <=< B64.decodeBase64 $ encodeUtf8 tbip70PaymentRequestJSON :: Bitcoin.PaymentRequest -> Valuebip70PaymentRequestJSON r =v1 . obj $[ "bip70_request".= object[ "payment_key" .= (r ^. Bitcoin.paymentRequestKey . Bitcoin._PaymentKey),"payment_request_protobuf_64" .= (r ^. Bitcoin.bip70Request . to protoBase64)]]parseBip70PaymentRequestJSON :: Value -> Parser Bitcoin.PaymentRequestparseBip70PaymentRequestJSON = \caseObject wrapper -> doo <- wrapper .: "bip70_request"Bitcoin.PaymentRequest<$> (Bitcoin.PaymentKey <$> o .: "paymentKey")<*> ( either (fail . toString) pure . fromBase64Proto =<< (o .: "payment_request_protobuf_64"))nonobject ->fail $ "Value " <> show nonobject <> " is not a JSON object."zip321PaymentRequestJSON :: Zip321.PaymentRequest -> Valuezip321PaymentRequestJSON r =v1 . obj $["zip321_request" .= (toJSON . Zip321.toURI $ r)]parseZip321PaymentRequestJSON :: Value -> Parser Zip321.PaymentRequestparseZip321PaymentRequestJSON = \caseObject o ->either fail pure . Zip321.parseURI =<< (o .: "zip321_request")nonobject ->fail $ "Value " <> show nonobject <> " is not a JSON object."nativeRequestJSON :: NativeRequest c -> ValuenativeRequestJSON = \caseBip70Request r -> bip70PaymentRequestJSON rZip321Request r -> zip321PaymentRequestJSON rbitcoinPaymentJSON :: NetworkMode -> Bitcoin.Payment -> ValuebitcoinPaymentJSON nmode bp =object[ "amount" .= (bp ^? Bitcoin.amount . _Just . _Satoshi),"txid" .= (bp ^. Bitcoin.txid),"address" .= addrText,"payment_key" .= (bp ^. Bitcoin.paymentKey . Bitcoin._PaymentKey),"payment_protobuf_64" .= (bp ^. Bitcoin.bip70Payment . to protoBase64)]whereaddrText = addrToText (getNetwork nmode) <$> (bp ^. Bitcoin.address)parseBitcoinPaymentJSON :: NetworkMode -> Value -> Parser Bitcoin.PaymentparseBitcoinPaymentJSON nmode = \caseObject o ->Bitcoin.Payment<$> (fmap Satoshi <$> o .:? "amount")<*> (o .:? "txid")<*> (traverse (parseBtcAddr nmode) =<< o .:? "address")<*> (Bitcoin.PaymentKey <$> o .: "paymentKey")<*> ( either (fail . unpack) pure . fromBase64Proto =<< (o .: "payment_protobuf_64"))nonobject ->fail $ "Value " <> show nonobject <> " is not a JSON object."zcashPaymentJSON :: Zcash.Payment -> ValuezcashPaymentJSON zp =v1 . obj $[ "amount" .= (zp ^. Zcash.amount . _Zatoshi),"txid" .= (zp ^. Zcash.txid . Zcash._TxId)]parseZcashPaymentJSON :: Value -> Parser Zcash.PaymentparseZcashPaymentJSON = \case(Object o) ->Zcash.Payment<$> (Zatoshi <$> o .: "amount")<*> (review Zcash._TxId <$> o .: "txid")val ->fail $ "Value " <> show val <> " is not a JSON object."paymentJSON :: NetworkMode -> Payment c -> ValuepaymentJSON nmode p =v1 . obj $[ "payment_request_id" .= idValue (paymentRequest . to getConst . _PaymentRequestId) p,"payment_date" .= view paymentDate p,"payment_value" .= nativePaymentValue]wherenativePaymentValue :: ValuenativePaymentValue = case view nativePayment p ofBitcoinPayment bp -> bitcoinPaymentJSON nmode bpZcashPayment bp -> zcashPaymentJSON bp - file addition: Projects.hs[5.6049]
{-# LANGUAGE QuasiQuotes #-}module Aftok.Database.PostgreSQL.Projects( createProject,listProjects,findProject,findUserProjects,addUserToProject,createInvitation,findInvitation,acceptInvitation,)whereimport Aftok.Database( InvitedUID,InvitingUID,)import Aftok.Database.PostgreSQL.Types( DBM,SerDepFunction (..),idParser,pexec,pinsert,pquery,ptransact,utcParser,)import Aftok.Project( Invitation (..),InvitationCode (..),Project (..),depf,inceptionDate,initiator,projectName,randomInvCode,renderInvCode,)import Aftok.Types( Email (..),ProjectId (..),UserId (..),_ProjectId,_UserId,)import Control.Lensimport Data.Aeson (toJSON)import qualified Data.Thyme.Time as Cimport Database.PostgreSQL.Simple (Only (..))import Database.PostgreSQL.Simple.FromField (fromJSONField)import Database.PostgreSQL.Simple.FromRow (RowParser, field, fieldWith)import Database.PostgreSQL.Simple.SqlQQ (sql)import Safe (headMay)import Prelude hiding (null)projectParser :: RowParser ProjectprojectParser =Project<$> field<*> utcParser<*> idParser UserId<*> (unSerDepFunction <$> fieldWith fromJSONField)invitationParser :: RowParser InvitationinvitationParser =Invitation<$> idParser ProjectId<*> idParser UserId<*> fmap Email field<*> utcParser<*> fmap (fmap C.toThyme) fieldcreateProject :: Project -> DBM ProjectIdcreateProject p =pinsertProjectId[sql| INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn)VALUES (?, ?, ?, ?) RETURNING id |]( p ^. projectName,p ^. (inceptionDate . to C.fromThyme),p ^. (initiator . _UserId),toJSON $ p ^. depf . to SerDepFunction)listProjects :: DBM [ProjectId]listProjects =pquery (idParser ProjectId) [sql| SELECT id FROM projects |] ()findProject :: ProjectId -> DBM (Maybe Project)findProject (ProjectId pid) =headMay<$> pqueryprojectParser[sql| SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ? |](Only pid)findUserProjects :: UserId -> DBM [(ProjectId, Project)]findUserProjects (UserId uid) =pquery((,) <$> idParser ProjectId <*> projectParser)[sql| SELECT DISTINCT ON (p.inception_date, p.id)p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fnFROM projects p LEFT OUTER JOIN project_companions pc ON pc.project_id = p.idWHERE pc.user_id = ?OR p.initiator_id = ?ORDER BY p.inception_date, p.id |](uid, uid)addUserToProject :: ProjectId -> InvitingUID -> InvitedUID -> DBM ()addUserToProject pid current new =void $pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?) |](pid ^. _ProjectId, new ^. _UserId, current ^. _UserId)createInvitation :: ProjectId -> InvitingUID -> Email -> C.UTCTime -> DBM InvitationCodecreateInvitation (ProjectId pid) (UserId uid) (Email e) t = doinvCode <- liftIO randomInvCodevoid $pexec[sql| INSERT INTO invitations (project_id, invitor_id, invitee_email, invitation_key, invitation_time)VALUES (?, ?, ?, ?, ?) |](pid, uid, e, renderInvCode invCode, C.fromThyme t)pure invCodefindInvitation :: InvitationCode -> DBM (Maybe Invitation)findInvitation ic =headMay<$> pqueryinvitationParser[sql| SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_timeFROM invitations WHERE invitation_key = ? |](Only $ renderInvCode ic)acceptInvitation :: UserId -> InvitationCode -> C.UTCTime -> DBM ()acceptInvitation (UserId uid) ic t = ptransact $ dovoid $pexec[sql| UPDATE invitations SET acceptance_time = ? WHERE invitation_key = ? |](C.fromThyme t, renderInvCode ic)void $pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by, joined_at)SELECT i.project_id, ?, i.invitor_id, ?FROM invitations iWHERE i.invitation_key = ? |](uid, C.fromThyme t, renderInvCode ic) - replacement in lib/Aftok/Database/PostgreSQL/Types.hs at line 1
module Aftok.Database.PostgreSQL.Types where{-# LANGUAGE GeneralizedNewtypeDeriving #-}module Aftok.Database.PostgreSQL.Types( DBM,SerDepFunction (..),pexec,pinsert,pquery,ptransact,askNetworkMode,idParser,utcParser,nullField,nominalDiffTimeParser,creditToParser,creditToName,bitcoinAddressParser,zcashAddressParser,zcashIvkParser,currencyAmountParser,btcAmountParser,zecAmountParser,currencyType,currencyValue,)where - edit in lib/Aftok/Database/PostgreSQL/Types.hs at line 28
import Aftok.Currency (Amount (..), Currency (..))import Aftok.Currency.Bitcoin (Satoshi (..), _Satoshi)import qualified Aftok.Currency.Bitcoin as Bitcoinimport Aftok.Currency.Zcash (Zatoshi (..), _Zatoshi)import qualified Aftok.Currency.Zcash as Zcashimport Aftok.Database (DBError) - replacement in lib/Aftok/Database/PostgreSQL/Types.hs at line 38
import Aftok.Types (DepreciationFunction)import Aftok.Types( AccountId (..),CreditTo (..),DepreciationFunction,ProjectId (..),UserId (..),)import Control.Lens ((^.)) - edit in lib/Aftok/Database/PostgreSQL/Types.hs at line 49
)import qualified Data.List as Limport qualified Data.Text as Timport Data.Thyme.Clock as Cimport Data.Thyme.Time as Cimport Data.UUID (UUID)import Database.PostgreSQL.Simple( Connection,Query,ResultError (Incompatible),ToRow,execute,fromOnly,query,queryWith,withTransaction,)import Database.PostgreSQL.Simple.FromField( FieldParser,ResultError (ConversionFailed),fromField,returnError,typename, - edit in lib/Aftok/Database/PostgreSQL/Types.hs at line 73
import Database.PostgreSQL.Simple.FromRow (RowParser, field, fieldWith)import Database.PostgreSQL.Simple.Types (Null)import qualified Haskoin.Address as Bitcoinimport qualified Haskoin.Constants as Bitcoin - edit in lib/Aftok/Database/PostgreSQL/Types.hs at line 85[5.6630]
type DBM a = ReaderT (Bitcoin.NetworkMode, Connection) (ExceptT DBError IO) apexec :: (ToRow d) => Query -> d -> DBM Int64pexec q d = doconn <- asks sndlift . lift $ execute conn q dpinsert :: (ToRow d) => (UUID -> r) -> Query -> d -> DBM rpinsert f q d = doconn <- asks sndids <- lift . lift $ query conn q dpure . f . fromOnly $ L.head idspquery :: (ToRow d) => RowParser r -> Query -> d -> DBM [r]pquery p q d = doconn <- asks sndlift . lift $ queryWith p conn q dptransact :: DBM a -> DBM aptransact rt = doenv <- asklift . ExceptT $ withTransaction (snd env) (runExceptT $ runReaderT rt env)askNetworkMode :: DBM Bitcoin.NetworkModeaskNetworkMode = asks fstidParser :: (UUID -> a) -> RowParser aidParser f = f <$> fieldutcParser :: RowParser C.UTCTimeutcParser = C.toThyme <$> fieldnullField :: RowParser NullnullField = fieldnominalDiffTimeParser :: FieldParser NominalDiffTimenominalDiffTimeParser f v = C.fromSeconds' <$> fromField f vcreditToName :: CreditTo -> TextcreditToName (CreditToAccount _) = "credit_to_account"creditToName (CreditToUser _) = "credit_to_user"creditToName (CreditToProject _) = "credit_to_project"creditToParser :: RowParser CreditTocreditToParser = join $ fieldWith creditToParser'creditToParser' :: FieldParser (RowParser CreditTo)creditToParser' f v = dotn <- typename fif tn /= "credit_to_t"then returnError Incompatible f "column was not of type credit_to_t"else maybe empty (pure . parser . decodeUtf8) vwhereparser :: Text -> RowParser CreditToparser = \case"credit_to_account" ->CreditToAccount <$> (idParser AccountId <* nullField <* nullField)"credit_to_user" ->CreditToUser <$> (nullField *> idParser UserId <* nullField)"credit_to_project" ->CreditToProject<$> (nullField *> nullField *> idParser ProjectId)_ -> emptybitcoinAddressParser :: Bitcoin.NetworkMode -> RowParser Bitcoin.AddressbitcoinAddressParser nmode =fieldWith $ addrFieldParser (Bitcoin.getNetwork nmode)whereaddrFieldParser :: Bitcoin.Network -> FieldParser Bitcoin.AddressaddrFieldParser n f v = dofieldValue <- fromField f vlet addrMay = Bitcoin.textToAddr n fieldValuelet err =returnErrorConversionFailedf( "could not deserialize value "<> T.unpack fieldValue<> " to a valid BTC address for network "<> show n)maybe err pure addrMaybtcAmountParser :: RowParser SatoshibtcAmountParser = (Satoshi . fromInteger) <$> fieldzecAmountParser :: RowParser ZatoshizecAmountParser = (Zatoshi . fromInteger) <$> fieldcurrencyAmountParser :: RowParser AmountcurrencyAmountParser = join $ fieldWith currencyAmountParser'currencyAmountParser' :: FieldParser (RowParser Amount)currencyAmountParser' f v = dotn <- typename fif tn /= "currency_t"then returnError Incompatible f "column was not of type currency_t"else maybe empty (pure . parser . decodeUtf8) vwhereparser :: Text -> RowParser Amountparser = \case"ZEC" -> Amount ZEC <$> zecAmountParser"BTC" -> Amount BTC <$> btcAmountParser_ -> empty-- TODO: address validation here?zcashAddressParser :: RowParser Zcash.AddresszcashAddressParser = Zcash.Address <$> field-- TODO: ivk validation here?zcashIvkParser :: RowParser Zcash.IVKzcashIvkParser = Zcash.IVK <$> fieldcurrencyType :: Amount -> TextcurrencyType = \caseAmount BTC _ -> "BTC"Amount ZEC _ -> "ZEC"currencyValue :: Amount -> Word64currencyValue = \caseAmount BTC sats -> sats ^. _SatoshiAmount ZEC zats -> zats ^. _Zatoshi - file addition: Users.hs[5.6049]
{-# LANGUAGE GADTs #-}{-# LANGUAGE GeneralizedNewtypeDeriving #-}{-# LANGUAGE LambdaCase #-}{-# LANGUAGE QuasiQuotes #-}module Aftok.Database.PostgreSQL.Users( createUser,findUser,findUserByName,findUserPaymentAddress,findAccountPaymentAddress,findAccountZcashIVK,)whereimport Aftok.Currency (Currency (..))import qualified Aftok.Currency.Zcash as Zcashimport Aftok.Database ()import Aftok.Database.PostgreSQL.Types( DBM,askNetworkMode,bitcoinAddressParser,idParser,pinsert,pquery,zcashAddressParser,zcashIvkParser,)import Aftok.Typesimport Control.Lensimport Database.PostgreSQL.Simpleimport Database.PostgreSQL.Simple.FromRowimport Database.PostgreSQL.Simple.SqlQQ( sql,)import Safe (headMay)import Prelude hiding (null)userParser :: RowParser UseruserParser = douname <- UserName <$> fieldremail <- fmap (RecoverByEmail . Email) <$> fieldrzaddr <- fmap (RecoverByZAddr . Zcash.Address) <$> fieldUser uname <$> maybe empty pure (remail <|> rzaddr)createUser :: User -> DBM UserIdcreateUser user' = dopinsertUserId[sql| INSERT INTO users (handle, recovery_email, recovery_zaddr)VALUES (?, ?, ?) RETURNING id |]( user' ^. (username . _UserName),user' ^? userAccountRecovery . _RecoverByEmail . _Email,user' ^? userAccountRecovery . _RecoverByZAddr . Zcash._Address)findUser :: UserId -> DBM (Maybe User)findUser (UserId uid) = doheadMay<$> pqueryuserParser[sql| SELECT handle, recovery_email, recovery_zaddr FROM users WHERE id = ? |](Only uid)findUserByName :: UserName -> DBM (Maybe (UserId, User))findUserByName (UserName h) = doheadMay<$> pquery((,) <$> idParser UserId <*> userParser)[sql| SELECT id, handle, recovery_email, recovery_zaddr FROM users WHERE handle = ? |](Only h)findUserPaymentAddress :: UserId -> Currency a c -> DBM (Maybe a)findUserPaymentAddress uid = \caseBTC -> domode <- askNetworkModeheadMay<$> pquery(bitcoinAddressParser mode)[sql| SELECT btc_addr FROM cryptocurrency_accountsWHERE user_id = ?AND currency = 'BTC'AND is_primary = true |](Only $ view _UserId uid)ZEC -> doheadMay<$> pquery(zcashAddressParser)[sql| SELECT zcash_addr FROM cryptocurrency_accountsWHERE user_id = ?AND currency = 'ZEC'AND is_primary = true |](Only $ view _UserId uid)findAccountPaymentAddress :: AccountId -> Currency a c -> DBM (Maybe a)findAccountPaymentAddress aid = \caseBTC -> domode <- askNetworkModeheadMay<$> pquery(bitcoinAddressParser mode)[sql| SELECT btc_addr FROM cryptocurrency_accountsWHERE id = ?AND btc_addr IS NOT NULL |](Only $ view _AccountId aid)ZEC -> doheadMay<$> pquery(zcashAddressParser)[sql| SELECT zcash_addr FROM cryptocurrency_accountsWHERE id = ?AND zcash_addr IS NOT NULL |](Only $ view _AccountId aid)-- TODO: rework this for the case where someone wants to-- use new diversified addresses for each purchase?findAccountZcashIVK :: AccountId -> DBM (Maybe Zcash.IVK)findAccountZcashIVK aid =headMay<$> pquery(zcashIvkParser)[sql| SELECT zcash_ivk FROM cryptocurrency_accountsWHERE id = ?AND zcash_ivk IS NOT NULL |](Only $ view _AccountId aid) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 12
import qualified Aftok.Auction as Aimport qualified Aftok.Billing as Bimport Aftok.Currency.Bitcoinimport Aftok.Currency.Zcash (ZAddr (..), _ZAddr)import qualified Aftok.Currency.Bitcoin as Bitcoin - replacement in lib/Aftok/Database/PostgreSQL.hs at line 14
import Aftok.Database.PostgreSQL.Types( SerDepFunction (..),)import Aftok.Intervalimport Aftok.Json( billableJSON,createSubscriptionJSON,paymentJSON,paymentRequestJSON,)import Aftok.Payments.Typesimport qualified Aftok.Project as Pimport Aftok.TimeLogimport Aftok.Typesimport Bippy.Types (Satoshi (..))import Control.Lensimport qualified Aftok.Database.PostgreSQL.Auctions as Qimport qualified Aftok.Database.PostgreSQL.Billing as Qimport qualified Aftok.Database.PostgreSQL.Events as Qimport qualified Aftok.Database.PostgreSQL.Projects as Qimport qualified Aftok.Database.PostgreSQL.Users as Q - edit in lib/Aftok/Database/PostgreSQL.hs at line 24
import Data.Aeson( Value,toJSON,)import Data.Hourglassimport qualified Data.List as Limport Data.ProtocolBuffers( decodeMessage,encodeMessage,)import Data.Serialize.Get (runGet)import Data.Serialize.Put (runPut)import qualified Data.Text as Timport Data.Thyme.Clock as Cimport Data.Thyme.Timeimport Data.UUID (UUID) - edit in lib/Aftok/Database/PostgreSQL.hs at line 25
import Database.PostgreSQL.Simple.FromFieldimport Database.PostgreSQL.Simple.FromRowimport Database.PostgreSQL.Simple.SqlQQ( sql,)import Database.PostgreSQL.Simple.Types( Null,)import Haskoin.Address( Address,addrToText,textToAddr,)import Haskoin.Constants (Network)import Safe (headMay) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 27
newtype QDBM a = QDBM (ReaderT (NetworkMode, Connection) (ExceptT DBError IO) a)newtype QDBM a = QDBM (ReaderT (Bitcoin.NetworkMode, Connection) (ExceptT DBError IO) a) - replacement in lib/Aftok/Database/PostgreSQL.hs at line 39
runQDBM :: NetworkMode -> Connection -> QDBM a -> ExceptT DBError IO arunQDBM :: Bitcoin.NetworkMode -> Connection -> QDBM a -> ExceptT DBError IO a - edit in lib/Aftok/Database/PostgreSQL.hs at line 41[5.224]→[5.1215:1216](∅→∅),[5.240]→[5.1215:1216](∅→∅),[5.7323]→[5.1215:1216](∅→∅),[5.15670]→[5.1215:1216](∅→∅),[5.1215]→[5.1215:1216](∅→∅),[5.1216]→[5.1472:1536](∅→∅),[5.1536]→[5.1461:1462](∅→∅),[5.1932]→[5.1461:1462](∅→∅),[5.1461]→[5.1461:1462](∅→∅),[5.1462]→[5.7324:7497](∅→∅),[5.7497]→[4.15638:15774](∅→∅),[4.15774]→[5.15795:15819](∅→∅),[5.15795]→[5.15795:15819](∅→∅),[5.15819]→[5.7630:7631](∅→∅),[5.7630]→[5.7630:7631](∅→∅),[5.7631]→[5.1370:1463](∅→∅),[5.1463]→[5.7718:7761](∅→∅),[5.7718]→[5.7718:7761](∅→∅),[5.7761]→[4.15775:15843](∅→∅),[4.15843]→[5.7829:7935](∅→∅),[5.15890]→[5.7829:7935](∅→∅),[5.7829]→[5.7829:7935](∅→∅),[5.7935]→[5.382:412](∅→∅),[5.412]→[5.58323:58363](∅→∅),[5.58363]→[4.15844:16087](∅→∅),[5.186]→[5.2131:2156](∅→∅),[5.564]→[5.2131:2156](∅→∅),[5.1307]→[5.2131:2156](∅→∅),[5.16035]→[5.2131:2156](∅→∅),[4.16087]→[5.2131:2156](∅→∅),[5.2131]→[5.2131:2156](∅→∅),[5.2156]→[5.1545:1546](∅→∅),[5.2363]→[5.1545:1546](∅→∅),[5.1545]→[5.1545:1546](∅→∅),[5.1546]→[5.2157:2234](∅→∅),[5.62]→[5.57:58](∅→∅),[5.568]→[5.57:58](∅→∅),[5.2234]→[5.57:58](∅→∅),[5.51]→[5.57:58](∅→∅),[5.58]→[5.2235:2298](∅→∅),[5.2298]→[5.66:114](∅→∅),[5.1910]→[5.66:114](∅→∅),[5.1832]→[5.114:213](∅→∅),[5.114]→[5.114:213](∅→∅),[5.213]→[5.1833:1923](∅→∅),[5.1923]→[4.16088:16328](∅→∅),[5.8188]→[5.2299:2353](∅→∅),[5.16250]→[5.2299:2353](∅→∅),[4.16328]→[5.2299:2353](∅→∅),[5.2025]→[5.2299:2353](∅→∅),[5.2353]→[5.16251:16312](∅→∅),[5.892]→[5.271:272](∅→∅),[5.1910]→[5.271:272](∅→∅),[5.2025]→[5.271:272](∅→∅),[5.2417]→[5.271:272](∅→∅),[5.16312]→[5.271:272](∅→∅),[5.271]→[5.271:272](∅→∅),[5.272]→[5.8189:8326](∅→∅),[5.8326]→[5.293:294](∅→∅),[5.293]→[5.293:294](∅→∅),[5.294]→[4.16329:16421](∅→∅),[4.16421]→[5.4201:4400](∅→∅),[5.16405]→[5.4201:4400](∅→∅),[5.4400]→[4.16422:16873](∅→∅),[5.1735]→[5.1409:1410](∅→∅),[5.2618]→[5.1409:1410](∅→∅),[5.4775]→[5.1409:1410](∅→∅),[4.16873]→[5.1409:1410](∅→∅),[5.17038]→[5.1409:1410](∅→∅),[5.1409]→[5.1409:1410](∅→∅),[5.1410]→[5.8868:8965](∅→∅),[5.8965]→[5.17039:17142](∅→∅),[5.424]→[5.2597:2598](∅→∅),[5.17142]→[5.2597:2598](∅→∅),[5.2597]→[5.2597:2598](∅→∅),[5.2598]→[4.16874:16959](∅→∅),[4.16959]→[5.9084:9109](∅→∅),[5.17228]→[5.9084:9109](∅→∅),[5.9084]→[5.9084:9109](∅→∅),[5.9109]→[5.17229:17303](∅→∅),[5.9171]→[5.2021:2022](∅→∅),[5.17303]→[5.2021:2022](∅→∅),[5.121]→[5.2021:2022](∅→∅),[5.2022]→[5.2619:2656](∅→∅),[5.2656]→[5.2023:2039](∅→∅),[5.652]→[5.2023:2039](∅→∅),[5.2039]→[5.17304:17439](∅→∅),[5.724]→[5.2729:2730](∅→∅),[5.2178]→[5.2729:2730](∅→∅),[5.2864]→[5.2729:2730](∅→∅),[5.2873]→[5.2729:2730](∅→∅),[5.17439]→[5.2729:2730](∅→∅),[5.2729]→[5.2729:2730](∅→∅),[5.2730]→[5.2874:2903](∅→∅),[5.2903]→[5.2040:2052](∅→∅),[5.752]→[5.2040:2052](∅→∅),[5.2052]→[5.17440:17520](∅→∅),[5.878]→[5.2828:2829](∅→∅),[5.2209]→[5.2828:2829](∅→∅),[5.2963]→[5.2828:2829](∅→∅),[5.3036]→[5.2828:2829](∅→∅),[5.17520]→[5.2828:2829](∅→∅),[5.2828]→[5.2828:2829](∅→∅),[5.2829]→[5.1542:1584](∅→∅),[5.1584]→[5.17521:17557](∅→∅),[5.9273]→[5.17521:17557](∅→∅),[5.17557]→[4.16960:17122](∅→∅),[5.1115]→[5.3061:3062](∅→∅),[5.1749]→[5.3061:3062](∅→∅),[4.17122]→[5.3061:3062](∅→∅),[5.17659]→[5.3061:3062](∅→∅),[5.3061]→[5.3061:3062](∅→∅),[5.3062]→[5.3037:3074](∅→∅),[5.3074]→[5.2208:2224](∅→∅),[5.1151]→[5.2208:2224](∅→∅),[5.2224]→[5.17660:17783](∅→∅),[5.460]→[5.313:314](∅→∅),[5.1244]→[5.313:314](∅→∅),[5.3209]→[5.313:314](∅→∅),[5.9417]→[5.313:314](∅→∅),[5.17783]→[5.313:314](∅→∅),[5.313]→[5.313:314](∅→∅),[5.314]→[5.3210:3253](∅→∅),[5.3253]→[5.2279:2298](∅→∅),[5.2317]→[5.2279:2298](∅→∅),[5.2298]→[5.17784:17927](∅→∅),[5.17927]→[5.3458:3459](∅→∅),[5.1361]→[5.3458:3459](∅→∅),[5.3459]→[5.3216:3255](∅→∅),[5.3255]→[5.3499:3516](∅→∅),[5.3499]→[5.3499:3516](∅→∅),[5.3516]→[5.17928:18161](∅→∅),[5.14078]→[5.3516:3624](∅→∅),[5.18161]→[5.3516:3624](∅→∅),[5.3516]→[5.3516:3624](∅→∅),[5.3624]→[5.2238:2308](∅→∅),[5.2308]→[4.17123:17164](∅→∅),[4.17164]→[5.2350:2408](∅→∅),[5.2350]→[5.2350:2408](∅→∅),[5.2408]→[4.17165:17293](∅→∅),[4.17293]→[5.3941:3989](∅→∅),[5.3941]→[5.3941:3989](∅→∅),[5.3989]→[5.1421:1442](∅→∅),[5.1442]→[5.18162:18233](∅→∅),[5.18233]→[5.3870:3913](∅→∅),[5.3913]→[5.18233:18296](∅→∅),[5.18233]→[5.18233:18296](∅→∅),[5.18296]→[5.2247:2297](∅→∅),[5.4146]→[5.2247:2297](∅→∅),[5.2297]→[5.1443:1466](∅→∅),[5.1466]→[5.18297:18451](∅→∅),[5.18451]→[4.17294:17348](∅→∅),[5.461]→[5.2528:2564](∅→∅),[4.17348]→[5.2528:2564](∅→∅),[5.18511]→[5.2528:2564](∅→∅),[5.2528]→[5.2528:2564](∅→∅),[5.2564]→[5.1467:1483](∅→∅),[5.1483]→[5.18512:18672](∅→∅),[5.285]→[5.293:294](∅→∅),[5.1361]→[5.293:294](∅→∅),[5.2726]→[5.293:294](∅→∅),[5.3748]→[5.293:294](∅→∅),[5.4146]→[5.293:294](∅→∅),[5.18672]→[5.293:294](∅→∅),[5.293]→[5.293:294](∅→∅),[5.294]→[5.2349:2396](∅→∅),[5.2396]→[5.505:527](∅→∅),[5.1410]→[5.505:527](∅→∅),[5.527]→[5.9494:9513](∅→∅),[5.9513]→[5.2532:2565](∅→∅),[5.1439]→[5.2532:2565](∅→∅),[5.1465]→[5.620:621](∅→∅),[5.2565]→[5.620:621](∅→∅),[5.620]→[5.620:621](∅→∅),[5.621]→[5.2397:2457](∅→∅),[5.2457]→[5.528:554](∅→∅),[5.1527]→[5.528:554](∅→∅),[5.554]→[5.9514:9533](∅→∅),[5.9533]→[4.17349:17387](∅→∅),[5.2605]→[5.1579:1614](∅→∅),[4.17387]→[5.1579:1614](∅→∅),[5.1579]→[5.1579:1614](∅→∅),[5.1614]→[5.1257:1258](∅→∅),[5.1257]→[5.1257:1258](∅→∅),[5.1258]→[5.1615:1676](∅→∅),[5.1676]→[5.555:580](∅→∅),[5.580]→[5.9534:9553](∅→∅),[5.9553]→[5.2620:2726](∅→∅),[5.2620]→[5.2620:2726](∅→∅),[5.2726]→[5.9554:9645](∅→∅)
idParser :: (UUID -> a) -> RowParser aidParser f = f <$> fieldnetworkIdParser :: FieldParser NetworkIdnetworkIdParser f b = donetworkName <- fromField f bcase networkName ofJust "btc" -> pure BTCJust "bch" -> pure BCHJust other ->returnErrorConversionFailedf("Network identifier " <> other <> " is not supported.")Nothing -> pure BTCbtcAddressParser :: NetworkMode -> RowParser (NetworkId, Address)btcAddressParser mode = donetworkId <- fieldWith (networkIdParser)address <- fieldWith $ addrFieldParser (toNetwork mode networkId)pure (networkId, address)addrFieldParser :: Network -> FieldParser AddressaddrFieldParser n f v = dofieldValue <- fromField f vlet addrMay = textToAddr n fieldValuelet err =returnErrorConversionFailedf( "could not deserialize value "<> T.unpack fieldValue<> " to a valid BTC address for network "<> show n)maybe err pure addrMaybtcParser :: RowParser SatoshibtcParser = (Satoshi . fromInteger) <$> fieldutcParser :: RowParser C.UTCTimeutcParser = toThyme <$> fieldnullField :: RowParser NullnullField = fieldeventTypeParser :: FieldParser (C.UTCTime -> LogEvent)eventTypeParser f v = dotn <- typename fif tn /= "event_t"then returnError Incompatible f "column was not of type event_t"elsemaybe(returnError UnexpectedNull f "event type may not be null")( maybe (returnError Incompatible f "unrecognized event type value") pure. nameEvent. decodeUtf8)vnominalDiffTimeParser :: FieldParser NominalDiffTimenominalDiffTimeParser f v = C.fromSeconds' <$> fromField f vcreditToParser :: NetworkMode -> RowParser (CreditTo (NetworkId, Address))creditToParser mode = join $ fieldWith (creditToParser' mode)creditToParser' ::NetworkMode -> FieldParser (RowParser (CreditTo (NetworkId, Address)))creditToParser' mode f v = dotn <- typename fif tn /= "credit_to_t"then returnError Incompatible f "column was not of type credit_to_t"else maybe empty (pure . parser . decodeUtf8) vwhereparser :: Text -> RowParser (CreditTo (NetworkId, Address))parser = \case"credit_to_address" ->CreditToCurrency <$> (btcAddressParser mode <* nullField <* nullField)"credit_to_user" ->CreditToUser <$> (nullField *> nullField *> idParser UserId <* nullField)"credit_to_project" ->CreditToProject<$> (nullField *> nullField *> nullField *> idParser ProjectId)_ -> emptylogEntryParser :: NetworkMode -> RowParser (LogEntry (NetworkId, Address))logEntryParser mode =LogEntry<$> creditToParser mode<*> (fieldWith eventTypeParser <*> utcParser)<*> fieldqdbLogEntryParser ::NetworkMode -> RowParser (KeyedLogEntry (NetworkId, Address))qdbLogEntryParser mode =(,,) <$> idParser ProjectId <*> idParser UserId <*> logEntryParser modeauctionParser :: RowParser A.AuctionauctionParser =A.Auction<$> idParser ProjectId<*> idParser UserId<*> utcParser<*> btcParser<*> utcParser<*> utcParserbidParser :: RowParser A.BidbidParser =A.Bid <$> idParser UserId <*> (Seconds <$> field) <*> btcParser <*> utcParseruserParser :: RowParser UseruserParser =User<$> (UserName <$> field)<*> ( (maybe empty pure =<< fmap (RecoverByEmail . Email) <$> field)<|> (maybe empty pure =<< fmap (RecoverByZAddr . ZAddr) <$> field))projectParser :: RowParser P.ProjectprojectParser =P.Project<$> field<*> utcParser<*> idParser UserId<*> (unSerDepFunction <$> fieldWith fromJSONField)invitationParser :: RowParser P.InvitationinvitationParser =P.Invitation<$> idParser ProjectId<*> idParser UserId<*> fmap Email field<*> utcParser<*> fmap (fmap toThyme) fieldbillableParser :: RowParser B.BillablebillableParser =B.Billable<$> idParser ProjectId<*> idParser UserId<*> field<*> field<*> recurrenceParser<*> btcParser<*> field<*> fieldWith (optionalField nominalDiffTimeParser)<*> field<*> fieldrecurrenceParser :: RowParser B.RecurrencerecurrenceParser =let prec :: Text -> RowParser B.Recurrenceprec = \case"annually" -> nullField *> pure B.Annually"monthly" -> B.Monthly <$> field--"semimonthly" = nullField *> pure B.SemiMonthly"weekly" -> B.Weekly <$> field"onetime" -> nullField *> pure B.OneTime_ -> emptyin field >>= precsubscriptionParser :: RowParser B.SubscriptionsubscriptionParser =B.Subscription<$> idParser UserId<*> idParser B.BillableId<*> (B.EmailChannel . Email <$> field)<*> (toThyme <$> field)<*> ((fmap toThyme) <$> field)paymentRequestParser :: RowParser PaymentRequestpaymentRequestParser =PaymentRequest<$> fmap B.SubscriptionId field<*> ((either (const empty) pure . runGet decodeMessage) =<< field)<*> fmap PaymentKey field<*> fmap toThyme field<*> fmap toThyme fieldpaymentParser :: RowParser PaymentpaymentParser =Payment<$> (PaymentRequestId <$> field)<*> (field >>= (either (const empty) pure . runGet decodeMessage))<*> (toThyme <$> field)<*> fieldpexec :: (ToRow d) => Query -> d -> QDBM Int64pexec q d = QDBM $ doconn <- asks sndlift . lift $ execute conn q dpinsert :: (ToRow d) => (UUID -> r) -> Query -> d -> QDBM rpinsert f q d = QDBM $ doconn <- asks sndids <- lift . lift $ query conn q dpure . f . fromOnly $ L.head idspquery :: (ToRow d) => RowParser r -> Query -> d -> QDBM [r]pquery p q d = QDBM $ doconn <- asks sndlift . lift $ queryWith p conn q dtransactQDBM :: QDBM a -> QDBM atransactQDBM (QDBM rt) = QDBM $ doenv <- asklift . ExceptT $ withTransaction (snd env) (runExceptT $ runReaderT rt env) - edit in lib/Aftok/Database/PostgreSQL.hs at line 42[5.1362]→[5.3749:3794](∅→∅),[5.3794]→[5.1377:1413](∅→∅),[5.1413]→[5.462:532](∅→∅),[5.532]→[4.17388:17672](∅→∅),[4.17672]→[5.747:847](∅→∅),[5.249]→[5.747:847](∅→∅),[5.351]→[5.4029:4052](∅→∅),[5.506]→[5.4029:4052](∅→∅),[5.4029]→[5.4029:4052](∅→∅),[5.4052]→[5.507:531](∅→∅),[5.531]→[5.848:917](∅→∅),[5.917]→[5.594:661](∅→∅),[5.594]→[5.594:661](∅→∅),[5.661]→[5.18937:18959](∅→∅),[5.18959]→[5.9664:9758](∅→∅),[5.9664]→[5.9664:9758](∅→∅),[5.9758]→[5.14269:14315](∅→∅),[5.14269]→[5.14269:14315](∅→∅),[5.14315]→[5.918:981](∅→∅),[5.806]→[5.918:981](∅→∅),[5.981]→[5.9759:9828](∅→∅),[5.854]→[5.425:426](∅→∅),[5.981]→[5.425:426](∅→∅),[5.1986]→[5.425:426](∅→∅),[5.3302]→[5.425:426](∅→∅),[5.4052]→[5.425:426](∅→∅),[5.9828]→[5.425:426](∅→∅),[5.425]→[5.425:426](∅→∅)
storeEvent :: DBOp a -> Maybe (QDBM EventId)storeEvent (CreateBillable uid b) =Just $ storeEventJSON (Just uid) "create_billable" (billableJSON b)storeEvent (CreateSubscription uid bid t) =Just $storeEventJSON(Just uid)"create_subscription"(createSubscriptionJSON uid bid t)storeEvent (CreatePaymentRequest req) =Just $storeEventJSON Nothing "create_payment_request" (paymentRequestJSON req)storeEvent (CreatePayment req) =Just $ storeEventJSON Nothing "create_payment" (paymentJSON req)storeEvent _ = Nothingtype EventType = TextstoreEventJSON :: Maybe UserId -> EventType -> Value -> QDBM EventIdstoreEventJSON uid t v = dotimestamp <- liftIO C.getCurrentTimepinsertEventId[sql| INSERT INTO aftok_events(event_time, created_by, event_type, event_json)VALUES (?, ?, ?, ?) RETURNING id |](fromThyme timestamp, preview (_Just . _UserId) uid, t, v)askNetworkMode :: QDBM NetworkModeaskNetworkMode = QDBM $ asks fst - replacement in lib/Aftok/Database/PostgreSQL.hs at line 43[5.4296]→[5.18960:19202](∅→∅),[5.19202]→[5.4:94](∅→∅),[5.94]→[5.10067:10167](∅→∅),[5.14449]→[5.10067:10167](∅→∅),[5.10167]→[5.14551:14581](∅→∅),[5.14551]→[5.14551:14581](∅→∅),[5.14581]→[4.17673:17852](∅→∅),[4.17852]→[5.19349:19357](∅→∅),[5.19349]→[5.19349:19357](∅→∅),[5.19357]→[4.17853:17941](∅→∅),[5.10399]→[5.14644:14718](∅→∅),[4.17941]→[5.14644:14718](∅→∅),[5.19438]→[5.14644:14718](∅→∅),[5.14644]→[5.14644:14718](∅→∅),[5.14718]→[5.10400:10500](∅→∅),[5.10500]→[5.14820:14850](∅→∅),[5.14820]→[5.14820:14850](∅→∅),[5.14850]→[4.17942:18180](∅→∅),[4.18180]→[5.10662:10816](∅→∅),[5.19648]→[5.10662:10816](∅→∅),[5.10662]→[5.10662:10816](∅→∅),[5.10816]→[5.15069:15099](∅→∅),[5.15069]→[5.15069:15099](∅→∅),[5.15099]→[4.18181:18331](∅→∅),[4.18331]→[5.10817:10880](∅→∅),[5.3406]→[5.10817:10880](∅→∅),[5.10880]→[4.18332:18428](∅→∅),[4.18428]→[5.565:694](∅→∅),[5.10964]→[5.565:694](∅→∅),[5.694]→[5.11057:11130](∅→∅),[5.11057]→[5.11057:11130](∅→∅),[5.11130]→[5.15347:15373](∅→∅),[5.15347]→[5.15347:15373](∅→∅),[5.15373]→[4.18429:18446](∅→∅),[4.18446]→[5.2262:2329](∅→∅),[5.1084]→[5.2262:2329](∅→∅),[5.2329]→[5.11190:11215](∅→∅),[5.11190]→[5.11190:11215](∅→∅),[5.11215]→[5.2330:2347](∅→∅),[5.2347]→[4.18447:18545](∅→∅),[4.18545]→[5.730:830](∅→∅),[5.730]→[5.730:830](∅→∅),[5.830]→[5.11362:11474](∅→∅),[5.19984]→[5.11362:11474](∅→∅),[5.11362]→[5.11362:11474](∅→∅),[5.11474]→[5.2373:2522](∅→∅),[5.2522]→[4.18546:18685](∅→∅),[4.18685]→[5.866:966](∅→∅),[5.866]→[5.866:966](∅→∅),[5.966]→[5.11624:11766](∅→∅),[5.20166]→[5.11624:11766](∅→∅),[5.11624]→[5.11624:11766](∅→∅),[5.11766]→[5.2587:2719](∅→∅),[5.2719]→[4.18686:18835](∅→∅),[4.18835]→[5.7432:7617](∅→∅),[5.1002]→[5.7432:7617](∅→∅),[5.7617]→[5.2794:2935](∅→∅),[5.2935]→[4.18836:18971](∅→∅),[4.18971]→[5.3058:3399](∅→∅),[5.3058]→[5.3058:3399](∅→∅),[5.3399]→[4.18972:19122](∅→∅),[4.19122]→[5.12081:12126](∅→∅),[5.20508]→[5.12081:12126](∅→∅),[5.12081]→[5.12081:12126](∅→∅),[5.12126]→[5.16476:16519](∅→∅),[5.16476]→[5.16476:16519](∅→∅),[5.16519]→[4.19123:19174](∅→∅),[4.19174]→[5.12127:12213](∅→∅),[5.2904]→[5.12127:12213](∅→∅),[5.4576]→[5.6673:6685](∅→∅),[5.12213]→[5.6673:6685](∅→∅),[5.6673]→[5.6673:6685](∅→∅),[5.6685]→[5.12214:12292](∅→∅),[5.12292]→[5.20559:20593](∅→∅),[5.20593]→[5.12318:12371](∅→∅),[5.12318]→[5.12318:12371](∅→∅),[5.12371]→[5.124:215](∅→∅),[5.215]→[5.16675:16725](∅→∅),[5.12444]→[5.16675:16725](∅→∅),[5.16675]→[5.16675:16725](∅→∅),[5.16725]→[4.19175:19319](∅→∅),[4.19319]→[5.20706:20716](∅→∅),[5.58431]→[5.20706:20716](∅→∅),[5.20706]→[5.20706:20716](∅→∅),[5.20716]→[4.19320:19434](∅→∅),[4.19434]→[5.12612:12687](∅→∅),[5.20821]→[5.12612:12687](∅→∅),[5.12612]→[5.12612:12687](∅→∅),[5.12687]→[5.16883:16933](∅→∅),[5.16883]→[5.16883:16933](∅→∅),[5.16933]→[4.19435:19623](∅→∅),[4.19623]→[5.12847:12919](∅→∅),[5.20999]→[5.12847:12919](∅→∅),[5.12847]→[5.12847:12919](∅→∅),[5.12919]→[5.17088:17138](∅→∅),[5.17088]→[5.17088:17138](∅→∅),[5.17138]→[4.19624:19830](∅→∅),[4.19830]→[5.12990:13039](∅→∅),[5.21199]→[5.12990:13039](∅→∅),[5.12990]→[5.12990:13039](∅→∅),[5.13039]→[5.17261:17304](∅→∅),[5.17261]→[5.17261:17304](∅→∅),[5.17304]→[4.19831:19872](∅→∅),[4.19872]→[5.13040:13084](∅→∅),[5.4862]→[5.13040:13084](∅→∅),[5.13084]→[4.19873:19988](∅→∅),[4.19988]→[5.1136:1232](∅→∅),[5.1136]→[5.1136:1232](∅→∅),[5.1232]→[5.13247:13330](∅→∅),[5.13247]→[5.13247:13330](∅→∅),[5.13330]→[5.17523:17557](∅→∅),[5.17523]→[5.17523:17557](∅→∅),[5.17557]→[4.19989:20006](∅→∅),[4.20006]→[5.8047:8077](∅→∅),[5.8047]→[5.8047:8077](∅→∅),[5.8077]→[4.20007:20144](∅→∅),[5.13435]→[5.17664:17710](∅→∅),[4.20144]→[5.17664:17710](∅→∅),[5.21452]→[5.17664:17710](∅→∅),[5.17664]→[5.17664:17710](∅→∅),[5.17710]→[4.20145:20479](∅→∅),[4.20479]→[5.13602:13626](∅→∅),[5.21761]→[5.13602:13626](∅→∅),[5.13602]→[5.13602:13626](∅→∅),[5.13626]→[5.17863:17889](∅→∅),[5.17863]→[5.17863:17889](∅→∅),[5.17889]→[4.20480:20670](∅→∅),[5.13731]→[5.17996:18045](∅→∅),[4.20670]→[5.17996:18045](∅→∅),[5.21943]→[5.17996:18045](∅→∅),[5.17996]→[5.17996:18045](∅→∅),[5.18045]→[4.20671:21079](∅→∅),[4.21079]→[5.13783:13814](∅→∅),[5.705]→[5.13783:13814](∅→∅),[5.58467]→[5.22326:22347](∅→∅),[5.14063]→[5.22326:22347](∅→∅),[5.22347]→[5.1750:1819](∅→∅),[5.1819]→[5.4:47](∅→∅),[5.47]→[4.21080:21244](∅→∅),[5.1944]→[5.9510:9516](∅→∅),[4.21244]→[5.9510:9516](∅→∅),[5.22421]→[5.9510:9516](∅→∅),[5.9510]→[5.9510:9516](∅→∅),[5.1369]→[5.14220:14256](∅→∅),[5.14256]→[4.21245:21389](∅→∅),[4.21389]→[5.14321:14363](∅→∅),[5.3219]→[5.14321:14363](∅→∅),[5.14363]→[4.21390:21570](∅→∅),[4.21570]→[5.2215:2265](∅→∅),[5.2215]→[5.2215:2265](∅→∅),[5.2265]→[5.14363:14388](∅→∅),[5.14363]→[5.14363:14388](∅→∅),[5.14388]→[4.21571:21735](∅→∅),[4.21735]→[5.14456:14528](∅→∅),[5.9826]→[5.14456:14528](∅→∅),[5.4993]→[5.9905:9941](∅→∅),[5.14528]→[5.9905:9941](∅→∅),[5.9905]→[5.9905:9941](∅→∅),[5.9941]→[4.21736:21864](∅→∅),[5.14651]→[5.18674:18710](∅→∅),[4.21864]→[5.18674:18710](∅→∅),[5.18674]→[5.18674:18710](∅→∅),[5.18710]→[4.21865:21923](∅→∅),[4.21923]→[5.10145:10160](∅→∅),[5.10145]→[5.10145:10160](∅→∅),[5.10160]→[4.21924:22092](∅→∅),[5.14779]→[5.18840:18895](∅→∅),[4.22092]→[5.18840:18895](∅→∅),[5.22692]→[5.18840:18895](∅→∅),[5.18840]→[5.18840:18895](∅→∅),[5.18895]→[4.22093:22127](∅→∅),[4.22127]→[5.5024:5088](∅→∅),[5.3742]→[5.5024:5088](∅→∅),[5.5088]→[4.22128:22377](∅→∅),[4.22377]→[5.14897:14976](∅→∅),[5.14897]→[5.14897:14976](∅→∅),[5.14976]→[5.19179:19219](∅→∅),[5.19179]→[5.19179:19219](∅→∅),[5.19219]→[4.22378:22567](∅→∅),[5.15090]→[5.19337:19383](∅→∅),[4.22567]→[5.19337:19383](∅→∅),[5.22862]→[5.19337:19383](∅→∅),[5.19337]→[5.19337:19383](∅→∅),[5.19383]→[4.22568:22728](∅→∅),[4.22728]→[5.19385:19407](∅→∅),[5.19385]→[5.19385:19407](∅→∅),[5.19407]→[5.23011:23077](∅→∅),[5.23077]→[4.22729:22818](∅→∅),[4.22818]→[5.15307:15388](∅→∅),[5.23161]→[5.15307:15388](∅→∅),[5.15307]→[5.15307:15388](∅→∅),[5.15388]→[5.19653:19689](∅→∅),[5.19653]→[5.19653:19689](∅→∅),[5.19689]→[4.22819:23289](∅→∅),[5.4013]→[5.15643:15762](∅→∅),[4.23289]→[5.15643:15762](∅→∅),[5.23571]→[5.15643:15762](∅→∅),[5.15643]→[5.15643:15762](∅→∅),[5.15762]→[5.4014:4091](∅→∅),[5.4091]→[4.23290:23527](∅→∅),[4.23527]→[5.15833:15871](∅→∅),[5.1043]→[5.15833:15871](∅→∅),[5.900]→[5.11916:11949](∅→∅),[5.5293]→[5.11916:11949](∅→∅),[5.15871]→[5.11916:11949](∅→∅),[5.11916]→[5.11916:11949](∅→∅),[5.11949]→[5.23795:23822](∅→∅),[5.23822]→[5.15895:16025](∅→∅),[5.15895]→[5.15895:16025](∅→∅),[5.16025]→[5.20437:20526](∅→∅),[5.20437]→[5.20437:20526](∅→∅),[5.20526]→[5.16026:16069](∅→∅),[5.16069]→[5.20570:20634](∅→∅),[5.20570]→[5.20570:20634](∅→∅),[5.20634]→[4.23528:23883](∅→∅),[5.3044]→[5.12451:12457](∅→∅),[5.5564]→[5.12451:12457](∅→∅),[5.20720]→[5.12451:12457](∅→∅),[4.23883]→[5.12451:12457](∅→∅),[5.12451]→[5.12451:12457](∅→∅),[5.12457]→[4.23884:24028](∅→∅),[5.23953]→[5.16211:16267](∅→∅),[4.24028]→[5.16211:16267](∅→∅),[5.16211]→[5.16211:16267](∅→∅),[5.16267]→[5.20884:21023](∅→∅),[5.20884]→[5.20884:21023](∅→∅),[5.21023]→[5.16268:16336](∅→∅),[5.16336]→[5.21092:21120](∅→∅),[5.21092]→[5.21092:21120](∅→∅),[5.21120]→[4.24029:24232](∅→∅),[5.24148]→[5.16500:16611](∅→∅),[4.24232]→[5.16500:16611](∅→∅),[5.16500]→[5.16500:16611](∅→∅),[5.16611]→[5.21401:21484](∅→∅),[5.21401]→[5.21401:21484](∅→∅),[5.21484]→[5.16612:16680](∅→∅),[5.16680]→[5.21553:21589](∅→∅),[5.21553]→[5.21553:21589](∅→∅),[5.21589]→[4.24233:24264](∅→∅),[4.24264]→[5.16713:16770](∅→∅),[5.3943]→[5.16713:16770](∅→∅),[5.566]→[5.576:609](∅→∅),[5.1286]→[5.576:609](∅→∅),[5.5675]→[5.576:609](∅→∅),[5.16770]→[5.576:609](∅→∅),[5.576]→[5.576:609](∅→∅),[5.609]→[5.24179:24210](∅→∅),[5.24210]→[5.16798:16889](∅→∅),[5.16798]→[5.16798:16889](∅→∅),[5.16889]→[5.21744:21790](∅→∅),[5.21744]→[5.21744:21790](∅→∅),[5.21790]→[4.24265:24375](∅→∅),[5.1354]→[5.647:653](∅→∅),[4.24375]→[5.647:653](∅→∅),[5.647]→[5.647:653](∅→∅),[5.653]→[4.24376:24530](∅→∅),[5.3982]→[5.16985:17016](∅→∅),[5.24400]→[5.16985:17016](∅→∅),[4.24530]→[5.16985:17016](∅→∅),[5.16985]→[5.16985:17016](∅→∅),[5.17016]→[5.21920:21948](∅→∅),[5.21920]→[5.21920:21948](∅→∅),[5.21948]→[4.24531:24761](∅→∅),[5.4062]→[5.17151:17263](∅→∅),[5.24607]→[5.17151:17263](∅→∅),[4.24761]→[5.17151:17263](∅→∅),[5.17151]→[5.17151:17263](∅→∅),[5.17263]→[5.22200:22234](∅→∅),[5.22200]→[5.22200:22234](∅→∅),[5.22234]→[4.24762:24802](∅→∅),[4.24802]→[5.17305:17349](∅→∅),[5.6109]→[5.17305:17349](∅→∅),[5.954]→[5.863:896](∅→∅),[5.1028]→[5.863:896](∅→∅),[5.6157]→[5.863:896](∅→∅),[5.17349]→[5.863:896](∅→∅),[5.863]→[5.863:896](∅→∅),[5.896]→[5.24647:24678](∅→∅),[5.24678]→[5.17377:17505](∅→∅),[5.17377]→[5.17377:17505](∅→∅),[5.17505]→[5.22393:22445](∅→∅),[5.22393]→[5.22393:22445](∅→∅),[5.22445]→[4.24803:25073](∅→∅),[5.3568]→[5.1180:1186](∅→∅),[4.25073]→[5.1180:1186](∅→∅),[5.1180]→[5.1180:1186](∅→∅),[5.1186]→[4.25074:25301](∅→∅),[4.25301]→[5.17674:17730](∅→∅),[5.17674]→[5.17674:17730](∅→∅),[5.17730]→[5.22674:22741](∅→∅),[5.22674]→[5.22674:22741](∅→∅),[5.22741]→[4.25302:25511](∅→∅),[4.25511]→[5.17813:17843](∅→∅),[5.17813]→[5.17813:17843](∅→∅),[5.17843]→[5.22955:22979](∅→∅),[5.22955]→[5.22955:22979](∅→∅),[5.22979]→[4.25512:25727](∅→∅),[4.25727]→[5.18000:18030](∅→∅),[5.18000]→[5.18000:18030](∅→∅),[5.18030]→[5.23183:23220](∅→∅),[5.23183]→[5.23183:23220](∅→∅),[5.23220]→[4.25728:25766](∅→∅),[4.25766]→[5.716:750](∅→∅),[5.4073]→[5.716:750](∅→∅),[5.750]→[5.765:846](∅→∅),[5.846]→[5.25012:25025](∅→∅),[5.25025]→[4.25767:25928](∅→∅),[4.25928]→[5.25196:25241](∅→∅),[5.25196]→[5.25196:25241](∅→∅),[5.25241]→[5.18079:18176](∅→∅),[5.18079]→[5.18079:18176](∅→∅),[5.18176]→[5.4063:4151](∅→∅),[5.4151]→[5.18247:18336](∅→∅),[5.18247]→[5.18247:18336](∅→∅),[5.18336]→[5.23531:23694](∅→∅),[5.23531]→[5.23531:23694](∅→∅),[5.23694]→[5.18337:18487](∅→∅),[5.18487]→[5.23847:23900](∅→∅),[5.23847]→[5.23847:23900](∅→∅),[5.23900]→[5.18488:18526](∅→∅),[5.18526]→[5.23939:24012](∅→∅),[5.23939]→[5.23939:24012](∅→∅),[5.24012]→[5.25242:25284](∅→∅),[5.1642]→[5.18527:18562](∅→∅),[5.1001]→[5.48:81](∅→∅),[5.1596]→[5.48:81](∅→∅),[5.4112]→[5.48:81](∅→∅),[5.6248]→[5.48:81](∅→∅),[5.18562]→[5.48:81](∅→∅),[5.48]→[5.48:81](∅→∅),[5.81]→[5.25285:25309](∅→∅),[5.25309]→[5.18583:18699](∅→∅),[5.18583]→[5.18583:18699](∅→∅),[5.18699]→[5.24152:24201](∅→∅),[5.24152]→[5.24152:24201](∅→∅),[5.24201]→[4.25929:26116](∅→∅),[5.394]→[5.339:345](∅→∅),[5.4351]→[5.339:345](∅→∅),[4.26116]→[5.339:345](∅→∅),[5.339]→[5.339:345](∅→∅),[5.345]→[4.26117:26273](∅→∅),[4.26273]→[5.18822:18844](∅→∅),[5.18822]→[5.18822:18844](∅→∅),[5.18844]→[5.24349:24389](∅→∅),[5.24349]→[5.24349:24389](∅→∅),[5.24389]→[4.26274:26312](∅→∅),[4.26312]→[5.6249:6294](∅→∅),[5.1045]→[5.6249:6294](∅→∅),[5.6294]→[5.3944:3945](∅→∅),[5.13039]→[5.3944:3945](∅→∅),[5.4454]→[5.3944:3945](∅→∅),[5.3945]→[5.13040:13152](∅→∅),[5.13152]→[5.3945:3946](∅→∅),[5.3945]→[5.3945:3946](∅→∅),[5.3946]→[5.13153:13185](∅→∅),[5.13185]→[5.24390:24424](∅→∅)
pgEval (CreateEvent (ProjectId pid) (UserId uid) (LogEntry c e m)) = case c ofCreditToCurrency (nid, addr) -> domode <- askNetworkModelet network = toNetwork mode nidpinsertEventId[sql| INSERT INTO work_events( project_id, user_id, credit_to_type, credit_to_network, credit_to_address, event_type, event_time, event_metadata )VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |]( pid,uid,creditToName c,renderNetworkId nid,addrToText network addr,eventName e,fromThyme $ e ^. eventTime,m)CreditToProject pid' ->pinsertEventId[sql| INSERT INTO work_events( project_id, user_id, credit_to_type, credit_to_project_id, event_type, event_time, event_metadata )VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |]( pid,uid,creditToName c,pid' ^. _ProjectId,eventName e,fromThyme $ e ^. eventTime,m)CreditToUser uid' ->pinsertEventId[sql| INSERT INTO work_events(project_id, user_id, credit_to_type, credit_to_user_id, event_type, event_time, event_metadata)VALUES (?, ?, ?, ?, ?, ?, ?)RETURNING id |]( pid,uid,creditToName c,uid' ^. _UserId,eventName e,fromThyme $ e ^. eventTime,m)pgEval (FindEvent (EventId eid)) = domode <- askNetworkModeheadMay<$> pquery(qdbLogEntryParser mode)[sql| SELECT project_id, user_id,credit_to_type,credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadata FROM work_eventsWHERE id = ? |](Only eid)pgEval (FindEvents (ProjectId pid) (UserId uid) rquery limit) = domode <- askNetworkModecase rquery of(Before e) ->pquery(logEntryParser mode)[sql| SELECT credit_to_type,credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time,event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time <= ?ORDER BY event_time DESCLIMIT ?|](pid, uid, fromThyme e, limit)(During s e) ->pquery(logEntryParser mode)[sql| SELECT credit_to_type,credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?AND event_time >= ? AND event_time <= ?ORDER BY event_time DESCLIMIT ?|](pid, uid, fromThyme s, fromThyme e, limit)(After s) ->pquery(logEntryParser mode)[sql| SELECT credit_to_type,credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ? AND event_time >= ?ORDER BY event_time DESCLIMIT ?|](pid, uid, fromThyme s, limit)(Always) ->pquery(logEntryParser mode)[sql| SELECT credit_to_type,credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time,event_metadataFROM work_eventsWHERE project_id = ? AND user_id = ?ORDER BY event_time DESCLIMIT ?|](pid, uid, limit)pgEval (AmendEvent (EventId eid) (TimeChange mt t)) =pinsertAmendmentId[sql| INSERT INTO event_time_amendments(event_id, amended_at, event_time)VALUES (?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, fromThyme t)pgEval (AmendEvent (EventId eid) (CreditToChange mt c)) = domode <- askNetworkModecase c ofCreditToCurrency (nid, addr) -> dolet network = toNetwork mode nidpinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_network, credit_to_address)VALUES (?, ?, ?, ?) RETURNING id |]( eid,fromThyme $ mt ^. _ModTime,creditToName c,renderNetworkId nid,addrToText network addr)CreditToProject pid ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_project_id)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, pid ^. _ProjectId)CreditToUser uid ->pinsertAmendmentId[sql| INSERT INTO event_credit_to_amendments(event_id, amended_at, credit_to_type, credit_to_user_id)VALUES (?, ?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, creditToName c, uid ^. _UserId)pgEval (AmendEvent (EventId eid) (MetadataChange mt v)) =pinsertAmendmentId[sql| INSERT INTO event_metadata_amendments(event_id, amended_at, event_metadata)VALUES (?, ?, ?) RETURNING id |](eid, fromThyme $ mt ^. _ModTime, v)pgEval (ReadWorkIndex (ProjectId pid)) = domode <- askNetworkModelogEntries <-pquery(logEntryParser mode)[sql| SELECT credit_to_type,credit_to_network, credit_to_address, credit_to_user_id, credit_to_project_id,event_type, event_time, event_metadataFROM work_eventsWHERE project_id = ? |](Only pid)pure $ workIndex logEntriespgEval (CreateAuction auc) =pinsertA.AuctionId[sql| INSERT INTO auctions (project_id, initiator_id, raise_amount, end_time)VALUES (?, ?, ?, ?) RETURNING id |]( auc ^. (A.projectId . _ProjectId),auc ^. (A.initiator . _UserId),auc ^. (A.raiseAmount . satoshi),auc ^. (A.auctionEnd . to fromThyme))pgEval (FindAuction aucId) =headMay<$> pqueryauctionParser[sql| SELECT project_id, initiator_id, created_at, raise_amount, start_time, end_timeFROM auctionsWHERE id = ? |](Only (aucId ^. A._AuctionId))pgEval (CreateBid (A.AuctionId aucId) bid) =pinsertA.BidId[sql| INSERT INTO bids (auction_id, bidder_id, bid_seconds, bid_amount, bid_time)VALUES (?, ?, ?, ?, ?) RETURNING id |]( aucId,bid ^. (A.bidUser . _UserId),case bid ^. A.bidSeconds of(Seconds i) -> i,bid ^. (A.bidAmount . satoshi),bid ^. (A.bidTime . to fromThyme))pgEval (FindBids aucId) =pquery((,) <$> idParser A.BidId <*> bidParser)[sql| SELECT id, bidder_id, bid_seconds, bid_amount, bid_time FROM bids WHERE auction_id = ? |](Only (aucId ^. A._AuctionId))pgEval (CreateUser user') = dopinsertUserId[sql| INSERT INTO users (handle, recovery_email, recovery_zaddr)VALUES (?, ?, ?) RETURNING id |]( user' ^. (username . _UserName),user' ^? userAccountRecovery . _RecoverByEmail . _Email,user' ^? userAccountRecovery . _RecoverByZAddr . _ZAddr)pgEval (FindUser (UserId uid)) = doheadMay<$> pqueryuserParser[sql| SELECT handle, recovery_email, recovery_zaddr FROM users WHERE id = ? |](Only uid)pgEval (FindUserByName (UserName h)) = doheadMay<$> pquery((,) <$> idParser UserId <*> userParser)[sql| SELECT id, handle, recovery_email, recovery_zaddr FROM users WHERE handle = ? |](Only h)pgEval (FindUserPaymentAddress (UserId uid)) = domode <- askNetworkModeheadMay<$> pquery(btcAddressParser mode)[sql| SELECT default_payment_network, default_payment_addr FROM users WHERE id = ? |](Only uid)pgEval (CreateInvitation (ProjectId pid) (UserId uid) (Email e) t) = doinvCode <- liftIO P.randomInvCodevoid $pexec[sql| INSERT INTO invitations (project_id, invitor_id, invitee_email, invitation_key, invitation_time)VALUES (?, ?, ?, ?, ?) |](pid, uid, e, P.renderInvCode invCode, fromThyme t)pure invCodepgEval (FindInvitation ic) =headMay<$> pqueryinvitationParser[sql| SELECT project_id, invitor_id, invitee_email, invitation_time, acceptance_timeFROM invitations WHERE invitation_key = ? |](Only $ P.renderInvCode ic)pgEval (AcceptInvitation (UserId uid) ic t) = transactQDBM $ dovoid $pexec[sql| UPDATE invitations SET acceptance_time = ? WHERE invitation_key = ? |](fromThyme t, P.renderInvCode ic)void $pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by, joined_at)SELECT i.project_id, ?, i.invitor_id, ?FROM invitations iWHERE i.invitation_key = ? |](uid, fromThyme t, P.renderInvCode ic)pgEval (CreateProject p) =pinsertProjectId[sql| INSERT INTO projects (project_name, inception_date, initiator_id, depreciation_fn)VALUES (?, ?, ?, ?) RETURNING id |]( p ^. P.projectName,p ^. (P.inceptionDate . to fromThyme),p ^. (P.initiator . _UserId),toJSON $ p ^. P.depf . to SerDepFunction)pgEval ListProjects =pquery (idParser ProjectId) [sql| SELECT id FROM projects |] ()pgEval (FindSubscribers pid) =pquery(idParser UserId)[sql| SELECT s.user_idFROM subscripions sJOIN billables b ON s.billable_id = b.idWHERE b.project_id = ? |](Only (pid ^. _ProjectId))pgEval (FindProject (ProjectId pid)) =headMay<$> pqueryprojectParser[sql| SELECT project_name, inception_date, initiator_id, depreciation_fn FROM projects WHERE id = ? |](Only pid)pgEval (FindUserProjects (UserId uid)) =pquery((,) <$> idParser ProjectId <*> projectParser)[sql| SELECT DISTINCT ON (p.inception_date, p.id) p.id, p.project_name, p.inception_date, p.initiator_id, p.depreciation_fnFROM projects p LEFT OUTER JOIN project_companions pc ON pc.project_id = p.idWHERE pc.user_id = ?OR p.initiator_id = ?ORDER BY p.inception_date, p.id |](uid, uid)pgEval (AddUserToProject pid current new) =void $pexec[sql| INSERT INTO project_companions (project_id, user_id, invited_by) VALUES (?, ?, ?) |](pid ^. _ProjectId, new ^. _UserId, current ^. _UserId)pgEval dbop@(CreateBillable _ b) = doeventId <- requireEventId dboppinsertB.BillableId[sql| INSERT INTO billables( project_id, event_id, name, description, recurrence_type, recurrence_count, billing_amount, grace_period_days, payment_request_email_template, payment_request_memo_template)VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) RETURNING id |]( b ^. (B.project . _ProjectId),eventId ^. _EventId,b ^. B.name,b ^. B.description,b ^. (B.recurrence . to B.recurrenceName),b ^. (B.recurrence . to B.recurrenceCount),b ^. (B.amount . satoshi),b ^. (B.gracePeriod),b ^. (B.paymentRequestEmailTemplate),b ^. (B.paymentRequestMemoTemplate))pgEval (FindBillable bid) =headMay<$> pquerybillableParser[sql| SELECT b.project_id, e.created_by, b.name, b.description,b.recurrence_type, b.recurrence_count,b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templateFROM billables b JOIN aftok_events e ON e.id = b.event_idWHERE b.id = ? |](Only (bid ^. B._BillableId))pgEval (FindBillables pid) =pquery((,) <$> idParser B.BillableId <*> billableParser)[sql| SELECT b.id, b.project_id, e.created_by, b.name, b.description,b.recurrence_type, b.recurrence_count,b.billing_amount, b.grace_period_daysb.payment_request_email_template, b.payment_request_memo_templateFROM billables b JOIN aftok_events e ON e.id = b.event_idWHERE b.project_id = ? |](Only (pid ^. _ProjectId))pgEval dbop@(CreateSubscription uid bid start_date) = doeventId <- requireEventId dboppinsertB.SubscriptionId[sql| INSERT INTO subscriptions(user_id, billable_id, event_id, start_date)VALUES (?, ?, ?, ?) RETURNING id |]( view _UserId uid,view B._BillableId bid,view _EventId eventId,fromThyme start_date)pgEval (FindSubscription sid) =headMay<$> pquerysubscriptionParser[sql| SELECT id, billable_id, contact_email, start_date, end_dateFROM subscriptions sWHERE s.id = ? |](Only (sid ^. B._SubscriptionId))pgEval (FindSubscriptions uid pid) =pquery((,) <$> idParser B.SubscriptionId <*> subscriptionParser)[sql| SELECT s.id, user_id, billable_id, contact_email, start_date, end_dateFROM subscriptions sJOIN billables b ON b.id = s.billable_idWHERE s.user_id = ?AND b.project_id = ? |](uid ^. _UserId, pid ^. _ProjectId)pgEval dbop@(CreatePaymentRequest req) = doeventId <- requireEventId dboppinsertPaymentRequestId[sql| INSERT INTO payment_requests(subscription_id, event_id, request_data, url_key, request_time, billing_date)VALUES (?, ?, ?, ?, ?, ?) RETURNING id |]( req ^. (subscription . B._SubscriptionId),eventId ^. _EventId,req ^. (paymentRequest . to (runPut . encodeMessage)),req ^. (paymentKey . _PaymentKey),req ^. (paymentRequestTime . to fromThyme),req ^. (billingDate . to fromThyme))pgEval (FindPaymentRequest (PaymentKey k)) =headMay<$> pquery((,) <$> idParser PaymentRequestId <*> paymentRequestParser)[sql| SELECT id, subscription_id, request_data, url_key, request_time, billing_dateFROM payment_requestsWHERE url_key = ?AND id NOT IN (SELECT payment_request_id FROM payments) |](Only k)pgEval (FindPaymentRequestId (PaymentRequestId prid)) =headMay<$> pquerypaymentRequestParser[sql| SELECT subscription_id, request_data, url_key, request_time, billing_dateFROM payment_requestsWHERE id = ? |](Only prid)pgEval (FindPaymentRequests sid) =pquery((,) <$> idParser PaymentRequestId <*> paymentRequestParser)[sql| SELECT id, subscription_id, request_data, url_key, request_time, billing_dateFROM payment_requestsWHERE subscription_id = ? |](Only (sid ^. B._SubscriptionId))pgEval (FindUnpaidRequests sid) =let rowp :: RowParser (PaymentKey, PaymentRequest, B.Subscription, B.Billable)rowp =(,,,)<$> (PaymentKey <$> field)<*> paymentRequestParser<*> subscriptionParser<*> billableParserin pqueryrowp[sql| SELECT r.url_key,r.subscription_id, r.request_data, r.url_key, r.request_time, r.billing_date,s.user_id, s.billable_id, s.contact_email, s.start_date, s.end_date,b.project_id, e.created_by, b.name, b.description, b.recurrence_type,b.recurrence_count, b.billing_amount, b.grace_period_days,b.payment_request_email_template, b.payment_request_memo_templateFROM payment_requests rJOIN subscriptions s on s.id = r.subscription_idJOIN billables b on b.id = s.billable_idJOIN aftok_events e on e.id = b.event_idWHERE subscription_id = ?AND r.id NOT IN (SELECT payment_request_id FROM payments) |](Only (sid ^. B._SubscriptionId))pgEval dbop@(CreatePayment p) = doeventId <- requireEventId dboppinsertPaymentId[sql| INSERT INTO payments(payment_request_id, event_id, payment_data, payment_date, exchange_rates)VALUES (?, ?, ?, ?, ?) RETURNING id |]( p ^. (request . _PaymentRequestId),eventId ^. _EventId,p ^. (payment . to (runPut . encodeMessage)),p ^. (paymentDate . to fromThyme),p ^. exchangeRates)pgEval (FindPayments rid) =pquery((,) <$> idParser PaymentId <*> paymentParser)[sql| SELECT id, payment_request_id, payment_data, payment_dateFROM paymentsWHERE payment_request_id = ? |](Only (rid ^. _PaymentRequestId))pgEval (RaiseDBError err _) = raiseError errrequireEventId :: DBOp a -> QDBM EventIdrequireEventId = maybe (raiseError EventStorageFailed) id . storeEventraiseError :: DBError -> QDBM araiseError = QDBM . lift . throwE[5.4296]pgEval =QDBM . \case(CreateEvent pid uid lentry) -> Q.createEvent pid uid lentry(FindEvent eid) -> Q.findEvent eid(FindEvents pid uid rquery limit) -> Q.findEvents pid uid rquery limit(AmendEvent eid amendment) -> Q.amendEvent eid amendment(ReadWorkIndex pid) -> Q.readWorkIndex pid(CreateAuction auc) -> Q.createAuction auc(FindAuction aucId) -> Q.findAuction aucId(CreateBid aucId bid) -> Q.createBid aucId bid(FindBids aucId) -> Q.findBids aucId(CreateUser user') -> Q.createUser user'(FindUser uid) -> Q.findUser uid(FindUserByName n) -> Q.findUserByName n(FindUserPaymentAddress uid currency) -> Q.findUserPaymentAddress uid currency(FindAccountPaymentAddress aid currency) -> Q.findAccountPaymentAddress aid currency(FindAccountZcashIVK aid) -> Q.findAccountZcashIVK aid(CreateProject p) -> Q.createProject pListProjects -> Q.listProjects(FindProject pid) -> Q.findProject pid(FindUserProjects uid) -> Q.findUserProjects uid(AddUserToProject pid current new) -> Q.addUserToProject pid current new(CreateInvitation pid uid e t) -> Q.createInvitation pid uid e t(FindInvitation ic) -> Q.findInvitation ic(AcceptInvitation uid ic t) -> Q.acceptInvitation uid ic tdbop@(CreateBillable uid b) -> doeventId <- Q.storeEvent' dbopQ.createBillable eventId uid b(FindBillable bid) -> Q.findBillable bid(FindBillables pid) -> Q.findBillables piddbop@(CreateSubscription uid bid start_date) -> doeventId <- Q.storeEvent' dbopQ.createSubscription eventId uid bid start_date(FindSubscription sid) -> Q.findSubscription sid(FindSubscriptions uid pid) -> Q.findSubscriptions uid pid(FindSubscribers pid) -> Q.findSubscribers piddbop@(StorePaymentRequest req) -> doeventId <- Q.storeEvent' dbopQ.storePaymentRequest eventId Nothing req(FindPaymentRequestByKey k) -> Q.findPaymentRequestByKey k(FindPaymentRequestById prid) -> Q.findPaymentRequestById prid(FindSubscriptionPaymentRequests sid) -> Q.findSubscriptionPaymentRequests sid(FindSubscriptionUnpaidRequests sid) -> Q.findSubscriptionUnpaidRequests siddbop@(CreatePayment p) -> doeventId <- Q.storeEvent' dbopQ.createPayment eventId p(FindPayments ccy rid) -> Q.findPayments ccy rid(RaiseDBError err _) -> lift . throwE $ err - replacement in lib/Aftok/Database.hs at line 11
import Aftok.Auction as Aimport qualified Aftok.Auction as A - replacement in lib/Aftok/Database.hs at line 13
import Aftok.Currency.Bitcoin (NetworkId)import Aftok.Intervalimport Aftok.Currency (Amount, Currency)import Aftok.Currency.Bitcoin.Payments (PaymentKey)import qualified Aftok.Currency.Zcash as Zcashimport Aftok.Interval (RangeQuery) - edit in lib/Aftok/Database.hs at line 18
( Payment,PaymentId,PaymentRequest,PaymentRequestId,SomePaymentRequestDetail,) - edit in lib/Aftok/Database.hs at line 26
( AmendmentId,EventAmendment,EventId,LogEntry,WorkIndex,) - replacement in lib/Aftok/Database.hs at line 33
import Aftok.Util( AccountId,Email,ProjectId,User,UserId,UserName,)import Aftok.Util (Program, fc, fromMaybeT) - replacement in lib/Aftok/Database.hs at line 47
import Data.AffineSpaceimport Data.AffineSpace ((.-.)) - edit in lib/Aftok/Database.hs at line 52
import Haskoin.Address (Address) - replacement in lib/Aftok/Database.hs at line 54
type KeyedLogEntry a = (ProjectId, UserId, LogEntry a)type KeyedLogEntry = (ProjectId, UserId, LogEntry) - edit in lib/Aftok/Database.hs at line 60
type BTCNet = (NetworkId, Address) - replacement in lib/Aftok/Database.hs at line 64
FindUserPaymentAddress :: UserId -> DBOp (Maybe (BTCNet))FindUserPaymentAddress :: UserId -> Currency a c -> DBOp (Maybe a)FindAccountPaymentAddress :: AccountId -> Currency a c -> DBOp (Maybe a)FindAccountZcashIVK :: AccountId -> DBOp (Maybe Zcash.IVK) - edit in lib/Aftok/Database.hs at line 70
FindSubscribers :: ProjectId -> DBOp [UserId] - replacement in lib/Aftok/Database.hs at line 75
CreateEvent :: ProjectId -> UserId -> LogEntry BTCNet -> DBOp EventIdAmendEvent :: EventId -> EventAmendment BTCNet -> DBOp AmendmentIdFindEvent :: EventId -> DBOp (Maybe (KeyedLogEntry BTCNet))FindEvents :: ProjectId -> UserId -> RangeQuery -> Word32 -> DBOp [LogEntry BTCNet]ReadWorkIndex :: ProjectId -> DBOp (WorkIndex BTCNet)CreateAuction :: Auction -> DBOp AuctionIdFindAuction :: AuctionId -> DBOp (Maybe Auction)CreateBid :: AuctionId -> Bid -> DBOp BidIdFindBids :: AuctionId -> DBOp [(BidId, Bid)]CreateBillable :: UserId -> Billable -> DBOp BillableIdFindBillable :: BillableId -> DBOp (Maybe Billable)FindBillables :: ProjectId -> DBOp [(BillableId, Billable)]CreateEvent :: ProjectId -> UserId -> LogEntry -> DBOp EventIdAmendEvent :: EventId -> EventAmendment -> DBOp AmendmentIdFindEvent :: EventId -> DBOp (Maybe KeyedLogEntry)FindEvents :: ProjectId -> UserId -> RangeQuery -> Word32 -> DBOp [LogEntry]ReadWorkIndex :: ProjectId -> DBOp WorkIndexCreateAuction :: A.Auction -> DBOp A.AuctionIdFindAuction :: A.AuctionId -> DBOp (Maybe A.Auction)CreateBid :: A.AuctionId -> A.Bid -> DBOp A.BidIdFindBids :: A.AuctionId -> DBOp [(A.BidId, A.Bid)]CreateBillable :: UserId -> Billable Amount -> DBOp BillableIdFindBillable :: BillableId -> DBOp (Maybe (Billable Amount))FindBillables :: ProjectId -> DBOp [(BillableId, Billable Amount)] - replacement in lib/Aftok/Database.hs at line 89
FindSubscriptions :: UserId -> ProjectId -> DBOp [(SubscriptionId, Subscription)]CreatePaymentRequest :: PaymentRequest -> DBOp PaymentRequestIdFindPaymentRequests :: SubscriptionId -> DBOp [(PaymentRequestId, PaymentRequest)]FindUnpaidRequests :: SubscriptionId -> DBOp [BillDetail]FindPaymentRequest :: PaymentKey -> DBOp (Maybe (PaymentRequestId, PaymentRequest))FindPaymentRequestId :: PaymentRequestId -> DBOp (Maybe PaymentRequest)CreatePayment :: Payment -> DBOp PaymentIdFindPayments :: PaymentRequestId -> DBOp [(PaymentId, Payment)]FindSubscriptions :: ProjectId -> UserId -> DBOp [(SubscriptionId, Subscription)]FindSubscribers :: ProjectId -> DBOp [UserId]StorePaymentRequest :: PaymentRequest c -> DBOp PaymentRequestIdFindPaymentRequestByKey :: PaymentKey -> DBOp (Maybe (PaymentRequestId, SomePaymentRequestDetail))FindPaymentRequestById :: PaymentRequestId -> DBOp (Maybe SomePaymentRequestDetail)FindSubscriptionPaymentRequests :: SubscriptionId -> DBOp [(PaymentRequestId, SomePaymentRequestDetail)]FindSubscriptionUnpaidRequests :: SubscriptionId -> DBOp [(PaymentRequestId, SomePaymentRequestDetail)]CreatePayment :: Payment c -> DBOp PaymentIdFindPayments :: Currency a c -> PaymentRequestId -> DBOp [(PaymentId, Payment c)] - edit in lib/Aftok/Database.hs at line 125
instance MonadDB m => MonadDB (ExceptT e m) whereliftdb = lift . liftdb - replacement in lib/Aftok/Database.hs at line 145
findUserPaymentAddress :: (MonadDB m) => UserId -> MaybeT m (BTCNet)findUserPaymentAddress = MaybeT . liftdb . FindUserPaymentAddressfindUserPaymentAddress :: (MonadDB m) => UserId -> Currency a c -> MaybeT m afindUserPaymentAddress uid n = MaybeT . liftdb $ FindUserPaymentAddress uid nfindAccountPaymentAddress :: (MonadDB m) => AccountId -> Currency a c -> MaybeT m afindAccountPaymentAddress uid n = MaybeT . liftdb $ FindAccountPaymentAddress uid n - replacement in lib/Aftok/Database.hs at line 230
(MonadDB m) => ProjectId -> UserId -> LogEntry BTCNet -> m EventId(MonadDB m) => ProjectId -> UserId -> LogEntry -> m EventId - replacement in lib/Aftok/Database.hs at line 234
(MonadDB m) => UserId -> EventId -> EventAmendment BTCNet -> m AmendmentId(MonadDB m) => UserId -> EventId -> EventAmendment -> m AmendmentId - replacement in lib/Aftok/Database.hs at line 245
findEvent :: (MonadDB m) => EventId -> m (Maybe (KeyedLogEntry BTCNet))findEvent :: (MonadDB m) => EventId -> m (Maybe KeyedLogEntry) - replacement in lib/Aftok/Database.hs at line 254
m [LogEntry BTCNet]m [LogEntry] - replacement in lib/Aftok/Database.hs at line 257
readWorkIndex :: (MonadDB m) => ProjectId -> UserId -> m (WorkIndex BTCNet)readWorkIndex :: (MonadDB m) => ProjectId -> UserId -> m WorkIndex - replacement in lib/Aftok/Database.hs at line 262
createBillable :: (MonadDB m) => UserId -> Billable -> m BillableIdcreateBillable :: (MonadDB m) => UserId -> Billable Amount -> m BillableId - replacement in lib/Aftok/Database.hs at line 266
findBillable :: (MonadDB m) => BillableId -> MaybeT m BillablefindBillable :: (MonadDB m) => BillableId -> MaybeT m (Billable Amount) - replacement in lib/Aftok/Database.hs at line 270[4.30032]→[4.30032:30107](∅→∅),[5.29467]→[5.5666:5729](∅→∅),[4.30107]→[5.5666:5729](∅→∅),[5.5666]→[5.5666:5729](∅→∅)
(MonadDB m) => UserId -> ProjectId -> m [(SubscriptionId, Subscription)]findSubscriptions uid pid = liftdb $ FindSubscriptions uid pid(MonadDB m) => ProjectId -> UserId -> m [(SubscriptionId, Subscription)]findSubscriptions pid uid = liftdb $ FindSubscriptions pid uid - replacement in lib/Aftok/Database.hs at line 274
(MonadDB m) => SubscriptionId -> MaybeT m (Subscription' UserId Billable)(MonadDB m) => SubscriptionId -> MaybeT m (Subscription' UserId (Billable Amount)) - replacement in lib/Aftok/Database.hs at line 279[5.4655]→[4.30213:30310](∅→∅),[5.29670]→[5.6032:6083](∅→∅),[4.30310]→[5.6032:6083](∅→∅),[5.6032]→[5.6032:6083](∅→∅)
findPaymentRequests ::(MonadDB m) => SubscriptionId -> m [(PaymentRequestId, PaymentRequest)]findPaymentRequests = liftdb . FindPaymentRequestsstorePaymentRequest ::(MonadDB m) => PaymentRequest c -> m PaymentRequestIdstorePaymentRequest = liftdb . StorePaymentRequestfindPaymentRequestByKey ::(MonadDB m) => PaymentKey -> MaybeT m (PaymentRequestId, SomePaymentRequestDetail)findPaymentRequestByKey = MaybeT . liftdb . FindPaymentRequestByKey - replacement in lib/Aftok/Database.hs at line 287[5.1595]→[4.30311:30408](∅→∅),[5.29768]→[5.25813:25871](∅→∅),[4.30408]→[5.25813:25871](∅→∅),[5.25813]→[5.25813:25871](∅→∅)
findPaymentRequest ::(MonadDB m) => PaymentKey -> MaybeT m (PaymentRequestId, PaymentRequest)findPaymentRequest = MaybeT . liftdb . FindPaymentRequestfindPaymentRequestById ::(MonadDB m) => PaymentRequestId -> MaybeT m SomePaymentRequestDetailfindPaymentRequestById = MaybeT . liftdb . FindPaymentRequestById - replacement in lib/Aftok/Database.hs at line 291[5.25872]→[4.30409:30494](∅→∅),[5.29854]→[5.25955:26017](∅→∅),[4.30494]→[5.25955:26017](∅→∅),[5.25955]→[5.25955:26017](∅→∅)
findPaymentRequestId ::(MonadDB m) => PaymentRequestId -> MaybeT m PaymentRequestfindPaymentRequestId = MaybeT . liftdb . FindPaymentRequestIdfindSubscriptionPaymentRequests ::(MonadDB m) => SubscriptionId -> m [(PaymentRequestId, SomePaymentRequestDetail)]findSubscriptionPaymentRequests = liftdb . FindSubscriptionPaymentRequests - replacement in lib/Aftok/Database.hs at line 297
findUnpaidRequests :: (MonadDB m) => SubscriptionId -> m [BillDetail]findUnpaidRequests = liftdb . FindUnpaidRequestsfindSubscriptionUnpaidRequests :: (MonadDB m) => SubscriptionId -> m [(PaymentRequestId, SomePaymentRequestDetail)]findSubscriptionUnpaidRequests = liftdb . FindSubscriptionUnpaidRequests - replacement in lib/Aftok/Database.hs at line 300
findPayment :: (MonadDB m) => PaymentRequestId -> MaybeT m PaymentfindPayment prid = MaybeT $ (fmap snd . headMay) <$> liftdb (FindPayments prid)findPayment :: (MonadDB m) => Currency a c -> PaymentRequestId -> MaybeT m (Payment c)findPayment currency prid = MaybeT $ (fmap snd . headMay) <$> liftdb (FindPayments currency prid) - replacement in lib/Aftok/Database.hs at line 305
createAuction :: (MonadDB m) => Auction -> m AuctionIdcreateAuction :: (MonadDB m) => A.Auction -> m A.AuctionId - replacement in lib/Aftok/Database.hs at line 309
findAuction :: (MonadDB m) => AuctionId -> UserId -> MaybeT m AuctionfindAuction :: (MonadDB m) => A.AuctionId -> UserId -> MaybeT m A.Auction - replacement in lib/Aftok/Database.hs at line 317
findAuction' :: (MonadDB m) => AuctionId -> UserId -> m AuctionfindAuction' :: (MonadDB m) => A.AuctionId -> UserId -> m A.Auction - replacement in lib/Aftok/Database.hs at line 328
createBid :: (MonadDB m) => AuctionId -> UserId -> Bid -> m BidIdcreateBid :: (MonadDB m) => A.AuctionId -> UserId -> A.Bid -> m A.BidId - replacement in lib/Aftok/Database.hs at line 333
if view bidTime bid > view auctionEnd aucif view A.bidTime bid > view A.auctionEnd auc - replacement in lib/Aftok/Json.hs at line 11
import Aftok.Auction as Aimport qualified Aftok.Auction as A - edit in lib/Aftok/Json.hs at line 13
import Aftok.Currency (Amount (..), Currency (..)) - edit in lib/Aftok/Json.hs at line 15
import Aftok.Currency.Zcash (_Zatoshi) - replacement in lib/Aftok/Json.hs at line 17
import Aftok.Paymentsimport Aftok.Project as Pimport Aftok.Payments.Types( PaymentId,_PaymentId,)import qualified Aftok.Project as P - edit in lib/Aftok/Json.hs at line 24
import Aftok.Util (traverseKeys) - edit in lib/Aftok/Json.hs at line 26
fromMaybeM, - edit in lib/Aftok/Json.hs at line 32
import qualified Data.ByteString.Base64 as B64 - edit in lib/Aftok/Json.hs at line 37
import Data.ProtocolBuffers (encodeMessage)import Data.Serialize.Put (runPut) - edit in lib/Aftok/Json.hs at line 45
addrFromJSON,addrToJSON, - replacement in lib/Aftok/Json.hs at line 142
projectJSON :: Project -> ValueprojectJSON :: P.Project -> Value - replacement in lib/Aftok/Json.hs at line 146
[ "projectName" .= (p ^. projectName),"inceptionDate" .= (p ^. inceptionDate),[ "projectName" .= (p ^. P.projectName),"inceptionDate" .= (p ^. P.inceptionDate), - replacement in lib/Aftok/Json.hs at line 151
qdbProjectJSON :: (ProjectId, Project) -> ValueqdbProjectJSON :: (ProjectId, P.Project) -> Value - replacement in lib/Aftok/Json.hs at line 154
auctionIdJSON :: AuctionId -> ValueauctionIdJSON = idJSON "auctionId" _AuctionIdauctionIdJSON :: A.AuctionId -> ValueauctionIdJSON = idJSON "auctionId" A._AuctionId - replacement in lib/Aftok/Json.hs at line 157
auctionJSON :: Auction -> ValueauctionJSON :: A.Auction -> Value - replacement in lib/Aftok/Json.hs at line 163
"raiseAmount" .= (x ^. (raiseAmount . satoshi))"raiseAmount" .= (x ^. (A.raiseAmount . _Satoshi)) - replacement in lib/Aftok/Json.hs at line 166
bidIdJSON :: BidId -> ValuebidIdJSON pid = v1 $ obj ["bidId" .= (pid ^. _BidId)]bidIdJSON :: A.BidId -> ValuebidIdJSON pid = v1 $ obj ["bidId" .= (pid ^. A._BidId)] - replacement in lib/Aftok/Json.hs at line 173[5.2561]→[5.21885:21955](∅→∅),[5.21955]→[4.34058:34256](∅→∅),[5.34058]→[5.22152:22188](∅→∅),[4.34256]→[5.22152:22188](∅→∅),[5.22152]→[5.22152:22188](∅→∅)
creditToJSON :: NetworkMode -> CreditTo (NetworkId, Address) -> ValuecreditToJSON nmode (CreditToCurrency (netId, addr)) =v2 $obj[ "creditToAddress" .= addrToJSON (toNetwork nmode netId) addr,"creditToNetwork" .= renderNetworkId netId]creditToJSON _ (CreditToUser uid) =creditToJSON :: CreditTo -> ValuecreditToJSON (CreditToAccount accountId) =v2 $ obj ["creditToAccount" .= idValue _AccountId accountId]creditToJSON (CreditToUser uid) = - replacement in lib/Aftok/Json.hs at line 178
creditToJSON _ (CreditToProject pid) =creditToJSON (CreditToProject pid) = - replacement in lib/Aftok/Json.hs at line 181
parseCreditTo :: NetworkMode -> Value -> Parser (CreditTo (NetworkId, Address))parseCreditTo nmode = unversion "CreditTo" $ \case(Version 1 0) -> parseCreditToV1 nmode(Version 2 0) -> parseCreditToV2 nmodeparseCreditTo :: Value -> Parser CreditToparseCreditTo = unversion "CreditTo" $ \case(Version 2 0) -> parseCreditToV2 - replacement in lib/Aftok/Json.hs at line 187
NetworkMode -> NetworkId -> Text -> Parser (CreditTo (NetworkId, Address))parseBtcAddr nmode net addrText =NetworkMode -> Text -> Parser AddressparseBtcAddr nmode addrText = - replacement in lib/Aftok/Json.hs at line 190
( fail. T.unpack$ "Address "<> addrText<> " cannot be parsed as a BTC network address.")(pure . CreditToCurrency . (net,))(textToAddr (toNetwork nmode net) addrText)(fail . T.unpack $ "Address " <> addrText <> " cannot be parsed as a BTC network address.")pure(textToAddr (getNetwork nmode) addrText) - replacement in lib/Aftok/Json.hs at line 194[5.22928]→[4.34660:34745](∅→∅),[5.34549]→[5.23017:23091](∅→∅),[4.34745]→[5.23017:23091](∅→∅),[5.23017]→[5.23017:23091](∅→∅),[5.23091]→[4.34746:34831](∅→∅),[5.34635]→[5.23174:23200](∅→∅),[4.34831]→[5.23174:23200](∅→∅),[5.23174]→[5.23174:23200](∅→∅),[5.23200]→[4.34832:35208](∅→∅)
parseCreditToV1 ::NetworkMode -> Object -> Parser (CreditTo (NetworkId, Address))parseCreditToV1 nmode x = doparseBtcAddr nmode BTC =<< x .: "btcAddr"parseCreditToV2 ::NetworkMode -> Object -> Parser (CreditTo (NetworkId, Address))parseCreditToV2 nmode o =let parseCreditToAddr = donetName <- o .: "creditToNetwork"net <-fromMaybeM(fail . T.unpack $ "Currency network " <> netName <> " not recognized.")(parseNetworkId netName)addrValue <- o .: "creditToAddress"CreditToCurrency. (net,)<$> addrFromJSON (toNetwork nmode net) addrValueparseCreditToV2 :: Object -> Parser CreditToparseCreditToV2 o =let parseCreditToAcct = dofmap CreditToAccount . parseId _AccountId =<< o .: "creditToAccount" - replacement in lib/Aftok/Json.hs at line 204
in parseCreditToAddrin parseCreditToAcct - replacement in lib/Aftok/Json.hs at line 213
payoutsJSON :: NetworkMode -> Payouts (NetworkId, Address) -> ValuepayoutsJSON nmode (Payouts m) =payoutsJSON :: FractionalPayouts -> ValuepayoutsJSON (Payouts m) = - replacement in lib/Aftok/Json.hs at line 216
let payoutsRec :: (CreditTo (NetworkId, Address), Rational) -> Valuelet payoutsRec :: (CreditTo, Rational) -> Value - replacement in lib/Aftok/Json.hs at line 218
object ["creditTo" .= creditToJSON nmode c, "payoutRatio" .= r]object ["creditTo" .= creditToJSON c, "payoutRatio" .= r, "payoutPercentage" .= (fromRational @Double r * 100)] - replacement in lib/Aftok/Json.hs at line 221
parsePayoutsJSON ::NetworkMode -> Value -> Parser (Payouts (NetworkId, Address))parsePayoutsJSON nmode = unversion "Payouts" $ pparsePayoutsJSON :: Value -> Parser FractionalPayoutsparsePayoutsJSON = unversion "Payouts" $ p - replacement in lib/Aftok/Json.hs at line 224
p :: Version -> Object -> Parser (Payouts (NetworkId, Address))p (Version 1 _) val =Payouts<$> join(traverseKeys (parseBtcAddr nmode BTC) <$> parseJSON (Object val))p :: Version -> Object -> Parser FractionalPayouts - replacement in lib/Aftok/Json.hs at line 228
<$> (parseCreditToV2 nmode =<< (x .: "creditTo"))<$> (parseCreditToV2 =<< (x .: "creditTo")) - replacement in lib/Aftok/Json.hs at line 239
workIndexJSON :: NetworkMode -> WorkIndex (NetworkId, Address) -> ValueworkIndexJSON nmode (WorkIndex widx) =workIndexJSON :: WorkIndex -> ValueworkIndexJSON (WorkIndex widx) = - replacement in lib/Aftok/Json.hs at line 244
widxRec :: (CreditTo (NetworkId, Address), NonEmpty Interval) -> ValuewidxRec :: (CreditTo, NonEmpty Interval) -> Value - replacement in lib/Aftok/Json.hs at line 247
[ "creditTo" .= creditToJSON nmode c,[ "creditTo" .= creditToJSON c, - replacement in lib/Aftok/Json.hs at line 258
logEntryJSON :: NetworkMode -> LogEntry (NetworkId, Address) -> ValuelogEntryJSON nmode le = v2 $ obj (logEntryFields nmode le)logEntryJSON :: LogEntry -> ValuelogEntryJSON le = v2 $ obj (logEntryFields le) - replacement in lib/Aftok/Json.hs at line 261
logEntryFields :: NetworkMode -> LogEntry (NetworkId, Address) -> [Pair]logEntryFields nmode (LogEntry c ev m) =[ "creditTo" .= creditToJSON nmode c,logEntryFields :: LogEntry -> [Pair]logEntryFields (LogEntry c ev m) =[ "creditTo" .= creditToJSON c, - edit in lib/Aftok/Json.hs at line 271
amountJSON :: Amount -> ValueamountJSON (Amount currency value) = case currency ofBTC -> object ["satoshi" .= (value ^. _Satoshi)]ZEC -> object ["zatoshi" .= (value ^. _Zatoshi)] - replacement in lib/Aftok/Json.hs at line 279
billableJSON :: B.Billable -> ValuebillableJSON :: B.Billable Amount -> Value - replacement in lib/Aftok/Json.hs at line 282
billableKV :: (KeyValue kv) => B.Billable -> [kv]billableKV :: (KeyValue kv) => B.Billable Amount -> [kv] - replacement in lib/Aftok/Json.hs at line 287
"recurrence" .= recurrenceJSON' (b ^. B.recurrence),"amount" .= (b ^. (B.amount . satoshi)),"recurrence" .= (b ^. B.recurrence . to recurrenceJSON'),"amount" .= (b ^. (B.amount . to amountJSON)), - replacement in lib/Aftok/Json.hs at line 290
"requestExpiryPeriod" .= (Clock.toSeconds' <$> (b ^. B.requestExpiryPeriod))"requestExpiryPeriod" .= (b ^. B.requestExpiryPeriod . to Clock.toSeconds') - replacement in lib/Aftok/Json.hs at line 293
qdbBillableJSON :: (B.BillableId, B.Billable) -> ValueqdbBillableJSON :: (B.BillableId, B.Billable Amount) -> Value - replacement in lib/Aftok/Json.hs at line 327[5.3562]→[5.630:676](∅→∅),[5.26949]→[5.630:676](∅→∅),[5.630]→[5.630:676](∅→∅),[5.676]→[5.3563:3694](∅→∅),[5.3694]→[4.37770:38065](∅→∅),[5.770]→[5.4007:4011](∅→∅),[4.38065]→[5.4007:4011](∅→∅),[5.4007]→[5.4007:4011](∅→∅),[5.4011]→[4.38066:38167](∅→∅),[5.2890]→[5.4011:4053](∅→∅),[5.3342]→[5.4011:4053](∅→∅),[5.37661]→[5.4011:4053](∅→∅),[4.38167]→[5.4011:4053](∅→∅),[5.4011]→[5.4011:4053](∅→∅),[5.4053]→[5.37662:37737](∅→∅),[5.4131]→[5.975:976](∅→∅),[5.37737]→[5.975:976](∅→∅),[5.975]→[5.975:976](∅→∅),[5.976]→[5.4132:4171](∅→∅),[5.4171]→[4.38168:38380](∅→∅)
paymentRequestJSON :: PaymentRequest -> ValuepaymentRequestJSON = v1 . obj . paymentRequestKVpaymentRequestKV :: (KeyValue kv) => PaymentRequest -> [kv]paymentRequestKV r =[ "subscription_id" .= idValue (subscription . B._SubscriptionId) r,"payment_request_protobuf_64" .= view prBytes r,"url_key" .= view (paymentKey . _PaymentKey) r,"payment_request_time" .= view paymentRequestTime r,"billing_date" .= view (billingDate . to showGregorian) r]whereprBytes =paymentRequest . to (T.decodeUtf8 . B64.encode . runPut . encodeMessage)billDetailsJSON :: [BillDetail] -> ValuebillDetailsJSON r = v1 $ obj ["payment_requests" .= fmap billDetailJSON r]billDetailJSON :: BillDetail -> ObjectbillDetailJSON r =obj $concat[ ["payment_request_id" .= view (_1 . _PaymentKey) r],paymentRequestKV $ view _2 r,subscriptionKV $ view _3 r,billableKV $ view _4 r]-- paymentRequestDetailsJSON :: [PaymentRequestDetail Amount] -> Value-- paymentRequestDetailsJSON r = v1 $ obj ["payment_requests" .= fmap paymentRequestDetailJSON r]---- paymentRequestDetailJSON :: PaymentRequestDetail Amount -> Object-- paymentRequestDetailJSON r = obj $ concat-- [ ["payment_request_id" .= view () r]-- , paymentRequestKV $ view _2 r-- , subscriptionKV $ view _3 r-- , billableKV $ view _4 r-- ] - edit in lib/Aftok/Json.hs at line 341[5.4378]→[5.976:1008](∅→∅),[5.976]→[5.976:1008](∅→∅),[5.1008]→[4.38381:38691](∅→∅),[5.3110]→[5.2430:2431](∅→∅),[5.3428]→[5.2430:2431](∅→∅),[5.38203]→[5.2430:2431](∅→∅),[4.38691]→[5.2430:2431](∅→∅),[5.2430]→[5.2430:2431](∅→∅)
paymentJSON :: Payment -> ValuepaymentJSON r =v1 $obj[ "payment_request_id" .= idValue (request . _PaymentRequestId) r,"payment_protobuf_64" .= view paymentBytes r,"payment_date" .= (r ^. paymentDate)]wherepaymentBytes =payment . to (T.decodeUtf8 . B64.encode . runPut . encodeMessage) - edit in lib/Aftok/Json.hs at line 354
NetworkMode -> - replacement in lib/Aftok/Json.hs at line 356
Parser (EventAmendment (NetworkId, Address))parseEventAmendment nmode t = unversion "EventAmendment" $ pParser EventAmendmentparseEventAmendment t = unversion "EventAmendment" $ p - replacement in lib/Aftok/Json.hs at line 359
p (Version 1 _) = parseEventAmendmentV1 nmode tp (Version 2 0) = parseEventAmendmentV2 nmode tp (Version 2 0) = parseEventAmendmentV2 t - edit in lib/Aftok/Json.hs at line 362[5.1957]→[4.39182:39296](∅→∅),[4.39296]→[5.25818:25852](∅→∅),[5.25818]→[5.25818:25852](∅→∅),[5.25852]→[4.39297:39697](∅→∅),[5.1362]→[5.2512:2513](∅→∅),[5.4328]→[5.2512:2513](∅→∅),[5.38771]→[5.2512:2513](∅→∅),[4.39697]→[5.2512:2513](∅→∅),[5.2512]→[5.2512:2513](∅→∅)
parseEventAmendmentV1 ::NetworkMode ->ModTime ->Object ->Parser (EventAmendment (NetworkId, Address))parseEventAmendmentV1 nmode t o =let parseA :: Text -> Parser (EventAmendment (NetworkId, Address))parseA "timeChange" = TimeChange t <$> o .: "eventTime"parseA "addrChange" = CreditToChange t <$> parseCreditToV1 nmode oparseA "metadataChange" = MetadataChange t <$> o .: "eventMeta"parseA tid =fail . T.unpack $ "Amendment type " <> tid <> " not recognized."in o .: "amendment" >>= parseA - edit in lib/Aftok/Json.hs at line 363
NetworkMode -> - replacement in lib/Aftok/Json.hs at line 365[4.39765]→[4.39765:39812](∅→∅),[4.39812]→[5.26197:26231](∅→∅),[5.26197]→[5.26197:26231](∅→∅),[5.26231]→[4.39813:39882](∅→∅)
Parser (EventAmendment (NetworkId, Address))parseEventAmendmentV2 nmode t o =let parseA :: Text -> Parser (EventAmendment (NetworkId, Address))Parser EventAmendmentparseEventAmendmentV2 t o =let parseA :: Text -> Parser EventAmendment - replacement in lib/Aftok/Json.hs at line 369
parseA "creditToChange" = CreditToChange t <$> parseCreditToV2 nmode oparseA "creditToChange" = CreditToChange t <$> parseCreditToV2 o - edit in lib/Aftok/Json.hs at line 376
NetworkMode -> - replacement in lib/Aftok/Json.hs at line 379
Parser (UTCTime -> (LogEntry (NetworkId, Address)))parseLogEntry nmode uid f = unversion "LogEntry" pParser (UTCTime -> LogEntry)parseLogEntry uid f = unversion "LogEntry" p - replacement in lib/Aftok/Json.hs at line 383
creditTo' <-o .:? "creditTo">>= maybe(pure $ CreditToUser uid)(parseCreditToV2 nmode)creditTo' <- o .:? "creditTo" >>= maybe (pure $ CreditToUser uid) (parseCreditToV2) - file addition: Bitcoin.hs[5.10523]
{-# LANGUAGE DeriveTraversable #-}{-# LANGUAGE TemplateHaskell #-}{-# LANGUAGE TupleSections #-}module Aftok.Payments.Bitcoin whereimport Aftok.Billing( Billable,amount,project,requestExpiryPeriod,)import Aftok.Currency (Currency (BTC))import Aftok.Currency.Bitcoin( NetworkMode,_Satoshi,getNetwork,)import Aftok.Currency.Bitcoin.Payments (PaymentKey (..), PaymentRequest (..))import Aftok.Database (MonadDB)import Aftok.Payments.Types( NativeRequest (Bip70Request),PaymentOps (..),PaymentRequestError,)import Aftok.Payments.Util (MinPayout (..), getPayouts, getProjectPayoutFractions)import qualified Bippy as Bimport qualified Bippy.Proto as Pimport Bippy.Types( Expiry (Expiry),Output (Output),PKIData,Satoshi (Satoshi),expiryTime,getExpires,getPaymentDetails,)import Control.Lens( (^.),makeLenses,)import Control.Monad.Except (throwError)import Control.Monad.Trans.Except (except, withExceptT)import qualified Crypto.PubKey.RSA.Types as RSA( Error (..),PrivateKey,)import Crypto.Random.Types( MonadRandom,getRandomBytes,)import Data.AffineSpace ((.+^))import Data.Map.Strict (assocs)import qualified Data.Text as Timport Data.Thyme.Clock as Cimport Data.Thyme.Time as Cimport Haskoin.Address (Address (..), encodeBase58Check)import Haskoin.Script (ScriptOutput (..))import Network.URI (URI)data BillingOps (m :: * -> *)= BillingOps{ -- | generator for user memomemoGen ::Billable Satoshi -> -- template for the billC.Day -> -- billing dateC.UTCTime -> -- payment request generation timem (Maybe Text),-- | generator for payment response URLuriGen ::PaymentKey -> -- payment key to be included in the URLm (Maybe URI),-- | generator for merchant payloadpayloadGen ::Billable Satoshi -> -- template for the billC.Day -> -- billing dateC.UTCTime -> -- payment request generation timem (Maybe ByteString)}data PaymentsConfig= PaymentsConfig{ _networkMode :: !NetworkMode,_signingKey :: !RSA.PrivateKey,_pkiData :: !PKIData,_minPayment :: !Satoshi}makeLenses ''PaymentsConfigdata PaymentError= RequestError !PaymentRequestError| SigningError !RSA.Error| IllegalAddress !Address{- Check whether the specified payment request has expired (whether wallet software- will still consider the payment request valid)-}isExpired :: C.UTCTime -> P.PaymentRequest -> BoolisExpired now req =let check = any ((now >) . C.toThyme . expiryTime)in -- using error here is reasonable since it would indicate-- a serialization problemeither (error . T.pack) (check . getExpires) $getPaymentDetails reqpaymentOps ::( MonadRandom m,MonadDB m) =>BillingOps m ->PaymentsConfig ->PaymentOps Satoshi (ExceptT PaymentError m)paymentOps ops cfg =PaymentOps{ newPaymentRequest = (((fmap Bip70Request) .) .) . bip70PaymentRequest ops cfg}bip70PaymentRequest ::( MonadRandom m,MonadDB m) =>BillingOps m ->PaymentsConfig ->-- | bill denominated in satoshiBillable Satoshi ->-- | billing base dateC.Day ->-- | time at which the bill is being issuedUTCTime ->ExceptT PaymentError m PaymentRequestbip70PaymentRequest ops cfg billable billingDay billingTime = dolet billTotal = billable ^. amountpayoutTime = C.mkUTCTime billingDay (fromInteger 0)payoutFractions <- lift $ getProjectPayoutFractions payoutTime (billable ^. project)payouts <- withExceptT RequestError $ getPayouts payoutTime BTC (MinPayout $ cfg ^. minPayment) billTotal payoutFractionsoutputs <- except $ traverse toOutput (assocs payouts)pkey <- PaymentKey . encodeBase58Check <$> lift (getRandomBytes 32)memo <- lift $ memoGen ops billable billingDay billingTimeuri <- lift $ uriGen ops pkeypayload <- lift $ payloadGen ops billable billingDay billingTimelet expiry = Expiry . C.fromThyme $ billingTime .+^ (billable ^. requestExpiryPeriod)let details =B.createPaymentDetails(getNetwork (cfg ^. networkMode))outputs(C.fromThyme billingTime)(Just expiry)memouripayloadresp <- lift $ B.createPaymentRequest (cfg ^. signingKey) (cfg ^. pkiData) detailseither (throwError . SigningError) (pure . PaymentRequest pkey) resptoOutput :: (Address, Satoshi) -> Either PaymentError OutputtoOutput (addr, amt) = case addr of(PubKeyAddress a) -> Right (Output amt (PayPKHash a))other -> Left $ IllegalAddress otheroutputAmount :: Satoshi -> Rational -> SatoshioutputAmount i r = Satoshi . round $ toRational (i ^. _Satoshi) * r - replacement in lib/Aftok/Payments/Types.hs at line 11
Subscription,SubscriptionId,Billable',BillableId,requestExpiryPeriod, - replacement in lib/Aftok/Payments/Types.hs at line 15
import qualified Bippy.Proto as Pimport Bippy.Types( Satoshi (..),expiryTime,getExpires,getPaymentDetails,)import Aftok.Currency (Currency (..), Currency' (..))import Aftok.Currency.Bitcoin (Satoshi)import qualified Aftok.Currency.Bitcoin.Payments as Bimport Aftok.Currency.Zcash (Zatoshi)import qualified Aftok.Currency.Zcash.Payments as Zimport qualified Aftok.Currency.Zcash.Zip321 as Zimport Aftok.Types (ProjectId, UserId) - replacement in lib/Aftok/Payments/Types.hs at line 23
( makeLenses,( (^.),makeLenses, - edit in lib/Aftok/Payments/Types.hs at line 26
view, - replacement in lib/Aftok/Payments/Types.hs at line 27
import Data.Aeson (Value)import qualified Data.Text as Timport Data.AffineSpace ((.+^)) - edit in lib/Aftok/Payments/Types.hs at line 31
import Haskoin.Address.Base58 (decodeBase58Check) - replacement in lib/Aftok/Payments/Types.hs at line 40
-- A unique identifier for the payment request, suitable-- for URL embedding.newtype PaymentKey = PaymentKey Text deriving (Eq)data NativeRequest currency whereBip70Request :: B.PaymentRequest -> NativeRequest SatoshiZip321Request :: Z.PaymentRequest -> NativeRequest Zatoshibip70Request :: NativeRequest currency -> Maybe B.PaymentRequestbip70Request = \caseBip70Request r -> Just r_ -> Nothingzip321Request :: NativeRequest currency -> Maybe Z.PaymentRequestzip321Request = \caseZip321Request r -> Just r_ -> Nothingdata NativePayment currency whereBitcoinPayment :: B.Payment -> NativePayment SatoshiZcashPayment :: Z.Payment -> NativePayment Zatoshi - replacement in lib/Aftok/Payments/Types.hs at line 58
makePrisms ''PaymentKeydata PaymentOps currency m= PaymentOps{ newPaymentRequest ::Billable currency -> -- billing informationC.Day -> -- payout date (billing date)C.UTCTime -> -- timestamp of payment request creationm (NativeRequest currency)} - replacement in lib/Aftok/Payments/Types.hs at line 67
data PaymentRequest' sdata PaymentRequest' (billable :: * -> *) currency - replacement in lib/Aftok/Payments/Types.hs at line 69
{ _subscription :: s,_paymentRequest :: P.PaymentRequest,_paymentKey :: PaymentKey,_paymentRequestTime :: C.UTCTime,_billingDate :: C.Day{ _billable :: billable currency,_createdAt :: C.UTCTime,_billingDate :: C.Day,_nativeRequest :: NativeRequest currency - edit in lib/Aftok/Payments/Types.hs at line 74
deriving (Functor, Foldable, Traversable) - edit in lib/Aftok/Payments/Types.hs at line 76
type PaymentRequest currency = PaymentRequest' (Const BillableId) currency - replacement in lib/Aftok/Payments/Types.hs at line 79
type PaymentRequest = PaymentRequest' SubscriptionIdtype PaymentRequestDetail currency = PaymentRequest' (Billable' ProjectId UserId) currency - replacement in lib/Aftok/Payments/Types.hs at line 81
data Payment' rdata SomePaymentRequest (b :: * -> *) = forall c. SomePaymentRequest (PaymentRequest' b c)type SomePaymentRequestDetail = SomePaymentRequest (Billable' ProjectId UserId)paymentRequestCurrency :: PaymentRequest' b c -> Currency' cpaymentRequestCurrency pr = case _nativeRequest pr ofBip70Request _ -> Currency' BTCZip321Request _ -> Currency' ZECisExpired :: forall c. UTCTime -> PaymentRequestDetail c -> BoolisExpired now req =let expiresAt = (req ^. createdAt) .+^ (req ^. (billable . requestExpiryPeriod))in now >= expiresAtdata Payment' (paymentRequest :: * -> *) currency - replacement in lib/Aftok/Payments/Types.hs at line 97
{ _request :: r,_payment :: P.Payment,{ _paymentRequest :: paymentRequest currency, - replacement in lib/Aftok/Payments/Types.hs at line 99
_exchangeRates :: Maybe Value_nativePayment :: NativePayment currency - edit in lib/Aftok/Payments/Types.hs at line 101
deriving (Functor, Foldable, Traversable) - replacement in lib/Aftok/Payments/Types.hs at line 104
type Payment = Payment' PaymentRequestIddata PaymentRequestError= AmountInvalid| NoRecipients - replacement in lib/Aftok/Payments/Types.hs at line 108
type BillDetail = (PaymentKey, PaymentRequest, Subscription, Billable)type Payment currency = Payment' (Const PaymentRequestId) currency - replacement in lib/Aftok/Payments/Types.hs at line 110[5.4643]→[5.6960:7098](∅→∅),[5.11452]→[5.6960:7098](∅→∅),[5.7098]→[4.42434:42496](∅→∅),[5.4706]→[5.2489:2509](∅→∅),[5.41047]→[5.2489:2509](∅→∅),[4.42496]→[5.2489:2509](∅→∅),[5.7149]→[5.2489:2509](∅→∅),[5.2509]→[5.3729:3782](∅→∅),[5.3782]→[4.42497:42699](∅→∅),[5.3880]→[5.2571:2572](∅→∅),[5.4793]→[5.2571:2572](∅→∅),[5.41250]→[5.2571:2572](∅→∅),[4.42699]→[5.2571:2572](∅→∅),[5.7377]→[5.2571:2572](∅→∅),[5.2572]→[5.1391:1441](∅→∅),[5.1441]→[5.41251:41338](∅→∅),[5.27198]→[5.28394:28498](∅→∅),[5.41338]→[5.28394:28498](∅→∅),[5.1513]→[5.28394:28498](∅→∅)
{- Check whether the specified payment request has expired (whether wallet software- will still consider the payment request valid)-}isExpired :: forall s. C.UTCTime -> PaymentRequest' s -> BoolisExpired now req =let check = any ((now >) . C.toThyme . expiryTime)in -- using error here is reasonable since it would indicate-- a serialization problemeither (error . T.pack) (check . getExpires) $getPaymentDetails (view paymentRequest req)parsePaymentKey :: ByteString -> Maybe PaymentKeyparsePaymentKey bs =(PaymentKey . decodeUtf8) <$> decodeBase58Check (decodeUtf8 bs)paymentRequestTotal :: P.PaymentRequest -> SatoshipaymentRequestTotal _ = error "Not yet implemented"[5.4643]type PaymentDetail currency = Payment' (PaymentRequest' (Billable' ProjectId UserId)) currency - file addition: Util.hs[5.10523]
{-# LANGUAGE TupleSections #-}module Aftok.Payments.Util whereimport Aftok.Currency (Currency, scaleCurrency)import Aftok.Database( DBOp( FindProject,ReadWorkIndex),MonadDB,findAccountPaymentAddress,findUserPaymentAddress,liftdb,raiseSubjectNotFound,)import Aftok.Payments.Types (PaymentRequestError (..))import Aftok.Project (depf)import qualified Aftok.TimeLog as TLimport Aftok.Types (ProjectId)import Control.Error.Util (note)import Control.Lens ((^.))import Control.Monad.Trans.Except (except)import Data.Map.Strict (assocs, fromListWith)import Data.Thyme.Clock as CgetProjectPayoutFractions ::(MonadDB m) =>C.UTCTime ->ProjectId ->m TL.FractionalPayoutsgetProjectPayoutFractions ptime pid = doproject' <-let projectOp = FindProject pidin maybe (raiseSubjectNotFound projectOp) pure =<< liftdb projectOpwidx <- liftdb $ ReadWorkIndex pidpure $ TL.payouts (TL.toDepF $ project' ^. depf) ptime widxnewtype MinPayout c = MinPayout cgetPayouts ::(MonadDB m, Ord a, Semigroup c, Ord c) =>-- | time used in computation of payouts when `creditTo` is another projectC.UTCTime ->-- | the currency with which the payment will be madeCurrency a c ->-- | the minimum payout amount, below which values are disregarded (avoids dust)MinPayout c ->-- | the amount to pay in totalc ->-- | the fractions of the total payout to pay to each recipientTL.FractionalPayouts ->ExceptT PaymentRequestError m (Map a c)getPayouts t currency mp@(MinPayout minAmt) amt payouts =if amt <= minAmtthen pure memptyelse do-- Multiply the total by each payout fraction. This may fail, so traverse.let scaled frac = note AmountInvalid $ scaleCurrency currency amt fracpayoutFractions <- except $ traverse scaled (payouts ^. TL._Payouts)fromListWith (<>) . join <$> traverse (uncurry (getPayoutAmounts t currency mp)) (assocs payoutFractions)getPayoutAmounts ::(MonadDB m, Ord a, Semigroup c, Ord c) =>-- | time used in computation of payouts when `creditTo` is another projectC.UTCTime ->-- | the network on which the payment will be madeCurrency a c ->-- | the minimum payout amount, below which amounts will be disregarded (avoids dust)MinPayout c ->-- | the recipient of the paymentTL.CreditTo ->-- | the amount to pay to the recipientc ->ExceptT PaymentRequestError m [(a, c)]getPayoutAmounts t network mp creditTo amt = case creditTo of(TL.CreditToAccount aid) ->fmap (,amt) . maybeToList <$> (lift . runMaybeT $ findAccountPaymentAddress aid network)(TL.CreditToUser uid) ->fmap (,amt) . maybeToList <$> (lift . runMaybeT $ findUserPaymentAddress uid network)(TL.CreditToProject pid) -> dopayouts <- lift $ getProjectPayoutFractions t pidassocs <$> getPayouts t network mp amt payouts - file addition: Zcash.hs[5.10523]
{-# LANGUAGE TemplateHaskell #-}module Aftok.Payments.Zcash whereimport Aftok.Billing( Billable,amount,messageText,project,)import Aftok.Currency (Currency (ZEC))import Aftok.Currency.Zcash (Address, Zatoshi)import Aftok.Currency.Zcash.Zip321 (PaymentItem (..), PaymentRequest (..))import Aftok.Database (MonadDB)import qualified Aftok.Payments.Types as PTimport Aftok.Payments.Util (MinPayout (..), getPayouts, getProjectPayoutFractions)import Control.Error.Safe (tryJust)import Control.Lens ((^.), makeLenses)import Data.Map.Strict (assocs)import Data.Thyme.Clock as Cimport Data.Thyme.Time as Cdata PaymentsConfig= PaymentsConfig{ _minAmt :: Zatoshi}makeLenses ''PaymentsConfigpaymentOps ::(MonadDB m) =>PaymentsConfig ->PT.PaymentOps Zatoshi (ExceptT PT.PaymentRequestError m)paymentOps cfg =PT.PaymentOps{ PT.newPaymentRequest = ((fmap PT.Zip321Request .) .) . zip321PaymentRequest cfg}zip321PaymentRequest ::(MonadDB m) =>PaymentsConfig ->-- | billing informationBillable Zatoshi ->-- | payout date (billing date)C.Day ->-- | timestamp for payment request creationC.UTCTime ->ExceptT PT.PaymentRequestError m PaymentRequestzip321PaymentRequest cfg billable billingDay _ = dolet payoutTime = C.mkUTCTime billingDay (fromInteger 0)billTotal = billable ^. amountpayoutFractions <- lift $ getProjectPayoutFractions payoutTime (billable ^. project)payouts <- getPayouts payoutTime ZEC (MinPayout $ cfg ^. minAmt) billTotal payoutFractionsPaymentRequest <$> (tryJust PT.NoRecipients $ nonEmpty (toPaymentItem <$> assocs payouts))wheretoPaymentItem :: (Address, Zatoshi) -> PaymentItemtoPaymentItem (a, z) =PaymentItem{ _address = a,_label = Nothing,_message = billable ^. messageText,_amount = z,_memo = Nothing, -- Just . Memo $ toASCIIBytes (reqid ^. PT._PaymentRequestId),_other = []} - replacement in lib/Aftok/Payments.hs at line 12
import Aftok.Currency.Bitcoin( NetworkId (..),NetworkMode,satoshi,toNetwork,( Billable,BillableId,Subscription,Subscription',SubscriptionId,amount, - edit in lib/Aftok/Payments.hs at line 19
import qualified Aftok.Billing as Bimport Aftok.Currency (Amount (..), Currency (..), Currency' (..)) - edit in lib/Aftok/Payments.hs at line 22
( DBOp( FindBillable,FindSubscription),MonadDB,OpForbiddenReason (UserNotSubscriber),findBillable,findPayment,findSubscriptionPaymentRequests,findSubscriptionUnpaidRequests,liftdb,raiseOpForbidden,raiseSubjectNotFound,storePaymentRequest,)import qualified Aftok.Payments.Bitcoin as BTC - replacement in lib/Aftok/Payments.hs at line 39
import Aftok.Project (depf)import qualified Aftok.TimeLog as TL( NativeRequest (..),Payment,PaymentOps (..),PaymentRequest,PaymentRequest' (..),PaymentRequestDetail,PaymentRequestId,SomePaymentRequest (..),SomePaymentRequestDetail,billingDate,isExpired,paymentRequestCurrency,)import qualified Aftok.Payments.Types as PTimport qualified Aftok.Payments.Zcash as Zcash - replacement in lib/Aftok/Payments.hs at line 55
( ProjectId,UserId,( UserId, - edit in lib/Aftok/Payments.hs at line 57
import qualified Bippy as Bimport qualified Bippy.Proto as Pimport qualified Bippy.Types as BT - replacement in lib/Aftok/Payments.hs at line 59
( (%~),( (.~), - edit in lib/Aftok/Payments.hs at line 61
makeClassy, - edit in lib/Aftok/Payments.hs at line 62
makeLenses, - edit in lib/Aftok/Payments.hs at line 65
view, - edit in lib/Aftok/Payments.hs at line 66
import Control.Lens.Tuple - replacement in lib/Aftok/Payments.hs at line 67
( MonadError,throwError,( throwError,withExceptT, - replacement in lib/Aftok/Payments.hs at line 70
import qualified Crypto.PubKey.RSA.Types as RSA( Error (..),PrivateKey,)import Crypto.Random.Types( MonadRandom,getRandomBytes,)import Data.AffineSpace ((.+^))import Data.Map.Strict (assocs)import qualified Crypto.Random.Types as CR - replacement in lib/Aftok/Payments.hs at line 73
import Haskoin.Address (Address (..))import Haskoin.Address.Base58 (encodeBase58Check)import Haskoin.Script (ScriptOutput (..))import Network.URIimport Network.URI () - replacement in lib/Aftok/Payments.hs at line 75
data PaymentsConfigdata PaymentsConfig m - replacement in lib/Aftok/Payments.hs at line 77
{ _networkMode :: !NetworkMode,_signingKey :: !RSA.PrivateKey,_pkiData :: !BT.PKIData{ _bitcoinBillingOps :: !(BTC.BillingOps m),_bitcoinPaymentsConfig :: !BTC.PaymentsConfig,_zcashPaymentsConfig :: !Zcash.PaymentsConfig - replacement in lib/Aftok/Payments.hs at line 82
makeClassy ''PaymentsConfigmakeLenses ''PaymentsConfig - replacement in lib/Aftok/Payments.hs at line 84[5.7760]→[4.44039:44757](∅→∅),[4.44757]→[5.8106:8107](∅→∅),[5.8106]→[5.8106:8107](∅→∅),[5.8107]→[5.4081:4107](∅→∅)
data BillingOps (m :: * -> *)= BillingOps{ -- | generator for user memomemoGen ::Subscription' UserId Billable -> -- subscription being billedT.Day -> -- billing dateC.UTCTime -> -- payment request generation timem (Maybe Text),-- | generator for payment response URLuriGen ::PaymentKey -> -- payment key to be included in the URLm (Maybe URI),-- | generator for merchant payloadpayloadGen ::Subscription' UserId Billable -> -- subscription being billedT.Day -> -- billing dateC.UTCTime -> -- payment request generation timem (Maybe ByteString)}data PaymentRequestStatusdata PaymentRequestStatus c - replacement in lib/Aftok/Payments.hs at line 86
Paid !PaymentPaid !(Payment c) - replacement in lib/Aftok/Payments.hs at line 88
Unpaid !PaymentRequestforall b. Unpaid !(PaymentRequest' b c) - replacement in lib/Aftok/Payments.hs at line 90
Expired !PaymentRequestforall b. Expired !(PaymentRequest' b c) - replacement in lib/Aftok/Payments.hs at line 93
= Overdue !SubscriptionId| SigningError !RSA.Error| IllegalAddress !Address= RequestError PT.PaymentRequestError| Overdue !PaymentRequestId| BTCPaymentError !BTC.PaymentError| BillableIdMismatch !BillableId !BillableId - edit in lib/Aftok/Payments.hs at line 99[5.8492]→[5.4108:4109](∅→∅),[5.4109]→[5.29782:29955](∅→∅),[5.29955]→[4.45018:45432](∅→∅),[5.7632]→[5.9049:9095](∅→∅),[4.45432]→[5.9049:9095](∅→∅),[5.13305]→[5.9049:9095](∅→∅),[5.9095]→[5.13366:13414](∅→∅),[5.13366]→[5.13366:13414](∅→∅),[5.13414]→[5.2838:2916](∅→∅)
{--- Find all the subscriptions for the specified customer, and- determine which if any are up for renewal. Create a payment- request for each such subscription.--}createPaymentRequests ::( MonadRandom m,MonadReader r m,HasPaymentsConfig r,MonadError e m,AsPaymentError e,MonadDB m) =>-- | generators for payment request componentsBillingOps m ->-- | timestamp for payment request creationC.UTCTime ->-- | customer responsible for paymentUserId ->-- | project whose worklog is to be paidProjectId ->m [PaymentRequestId]createPaymentRequests ops now custId pid = dosubscriptions <- findSubscriptions custId pidjoin <$> traverse (createSubscriptionPaymentRequests ops now) subscriptions - replacement in lib/Aftok/Payments.hs at line 101
( MonadRandom m,MonadReader r m,HasPaymentsConfig r,MonadError e m,AsPaymentError e,MonadDB m) =>BillingOps m ->forall m.(MonadDB m, CR.MonadRandom m) =>PaymentsConfig m -> - replacement in lib/Aftok/Payments.hs at line 106[4.45667]→[4.45667:45690](∅→∅),[4.45690]→[5.2917:2975](∅→∅),[5.9458]→[5.2917:2975](∅→∅),[5.2975]→[5.7785:7802](∅→∅),[5.7802]→[4.45691:45808](∅→∅),[5.7919]→[5.9653:9698](∅→∅),[5.30120]→[5.9653:9698](∅→∅),[4.45808]→[5.9653:9698](∅→∅),[5.9653]→[5.9653:9698](∅→∅)
m [PaymentRequestId]createSubscriptionPaymentRequests ops now (sid, sub) = dobillableSub <-maybeT (raiseSubjectNotFound . FindBillable $ sub ^. billable) pure $traverseOf billable findBillable subpaymentRequests <- findPaymentRequests sidExceptT PaymentError m [(PaymentRequestId, SomePaymentRequestDetail)]createSubscriptionPaymentRequests cfg now (sid, sub) = do-- fill in the billable for the subscriptionsub' <-lift . maybeT (raiseSubjectNotFound . FindBillable $ billableId) pure $traverseOf B.billable findBillable sub-- get previous payment requests & augment with billable informationpaymentRequests <- lift $ findSubscriptionPaymentRequests sid-- find dates for which no bill has yet been issued - replacement in lib/Aftok/Payments.hs at line 116[4.45828]→[5.7941:8011](∅→∅),[5.7941]→[5.7941:8011](∅→∅),[5.8011]→[4.45829:45905](∅→∅),[5.8083]→[5.2976:3048](∅→∅),[4.45905]→[5.2976:3048](∅→∅),[5.9870]→[5.2976:3048](∅→∅)
findUnbilledDates now (view billable billableSub) paymentRequests$ takeWhile (< view _utctDay now)$ billingSchedule billableSubtraverse (createPaymentRequest ops now sid billableSub) billableDatesfindUnbilledDates now paymentRequests. takeWhile (< now ^. _utctDay)$ B.billingSchedule sub'traverse (createPaymentRequest' sub') billableDateswherebillableId = sub ^. B.billable-- create a payment request for the specified unbilled datecreatePaymentRequest' ::Subscription' UserId (Billable Amount) ->T.Day ->ExceptT PaymentError m (PaymentRequestId, SomePaymentRequestDetail)createPaymentRequest' sub' day =let bill = sub' ^. B.billablein case bill ^. amount ofAmount BTC sats -> withExceptT BTCPaymentError $ dolet ops = BTC.paymentOps (cfg ^. bitcoinBillingOps) (cfg ^. bitcoinPaymentsConfig)bill' = bill & amount .~ satssecond SomePaymentRequest <$> createPaymentRequest ops now billableId bill' dayAmount ZEC zats -> withExceptT RequestError $ dolet ops = Zcash.paymentOps (cfg ^. zcashPaymentsConfig)bill' = bill & amount .~ zatssecond SomePaymentRequest <$> createPaymentRequest ops now billableId bill' day - replacement in lib/Aftok/Payments.hs at line 140
( MonadRandom m,MonadReader r m,HasPaymentsConfig r,MonadError e m,AsPaymentError e,MonadDB m) =>BillingOps m ->(MonadDB m) =>PaymentOps currency m -> - replacement in lib/Aftok/Payments.hs at line 143
SubscriptionId ->Subscription' UserId Billable ->BillableId ->Billable currency -> - replacement in lib/Aftok/Payments.hs at line 146[4.46157]→[4.46157:46178](∅→∅),[4.46178]→[5.3049:3096](∅→∅),[5.10233]→[5.3049:3096](∅→∅),[5.3096]→[4.46179:46192](∅→∅),[5.8240]→[5.946:1016](∅→∅),[4.46192]→[5.946:1016](∅→∅),[5.13427]→[5.946:1016](∅→∅),[5.1016]→[4.46193:46316](∅→∅),[4.46316]→[5.3260:3301](∅→∅),[5.3260]→[5.3260:3301](∅→∅),[5.3301]→[5.10377:10455](∅→∅),[5.10377]→[5.10377:10455](∅→∅),[5.10455]→[4.46317:46461](∅→∅),[4.46461]→[5.3302:3373](∅→∅),[5.10604]→[5.3302:3373](∅→∅)
m PaymentRequestIdcreatePaymentRequest ops now sid sub bday = docfg <- ask-- TODO: maybe make pkey a function of subscription, billable, bdaypkey <- PaymentKey . encodeBase58Check <$> getRandomBytes 32memo <- memoGen ops sub bday nowuri <- uriGen ops pkeypayload <- payloadGen ops sub bday nowdetails <- createPaymentDetails bday now memo uri payload (sub ^. billable)reqErr <- B.createPaymentRequest (cfg ^. signingKey) (cfg ^. pkiData) detailsreq <- either (throwError . review _SigningError) pure reqErrliftdb $ CreatePaymentRequest (PaymentRequest sid req pkey now bday)m (PaymentRequestId, PaymentRequestDetail currency)createPaymentRequest ops now billId bill bday = donativeReq <- newPaymentRequest ops bill bday nowlet req =PaymentRequest{ _billable = (Const billId),_createdAt = now,_billingDate = bday,_nativeRequest = nativeReq}reqId <- storePaymentRequest reqpure (reqId, req & PT.billable .~ bill) - replacement in lib/Aftok/Payments.hs at line 164
(MonadDB m, MonadError e m, AsPaymentError e) =>(MonadDB m) => - edit in lib/Aftok/Payments.hs at line 167
Billable -> - replacement in lib/Aftok/Payments.hs at line 168
[(PaymentRequestId, PaymentRequest)] ->[(PaymentRequestId, PT.SomePaymentRequestDetail)] -> - replacement in lib/Aftok/Payments.hs at line 172[4.46847]→[4.46847:46859](∅→∅),[4.46859]→[5.8632:8686](∅→∅),[5.8632]→[5.8632:8686](∅→∅),[5.8686]→[5.11373:11421](∅→∅),[5.11373]→[5.11373:11421](∅→∅),[5.11421]→[4.46860:47250](∅→∅),[5.9008]→[5.4542:4595](∅→∅),[4.47250]→[5.4542:4595](∅→∅),[5.11842]→[5.4542:4595](∅→∅),[5.4595]→[5.11896:11973](∅→∅),[5.11896]→[5.11896:11973](∅→∅)
m [T.Day]findUnbilledDates now b (px@(p : ps)) (dx@(d : ds)) =case compare (view (_2 . billingDate) p) d ofEQ ->getRequestStatus now p >>= \s -> case s ofExpired r ->if view _utctDay now > addDays (view gracePeriod b) (view billingDate r)then throwError (review _Overdue (r ^. subscription))else fmap (d :) $ findUnbilledDates now b px dx -- d will be rebilled_ -> findUnbilledDates now b ps ds -- if paid or unpaid, nothing to doGT -> fmap (d :) $ findUnbilledDates now b px dsLT -> findUnbilledDates now b ps dxfindUnbilledDates _ _ _ ds = pure dsExceptT PaymentError m [T.Day]findUnbilledDates now (px@((reqId, SomePaymentRequest req) : ps)) (dx@(d : ds)) =let rec = findUnbilledDates nowgracePeriod = req ^. PT.billable . B.gracePeriodin case compare (req ^. billingDate) d ofEQ ->lift (getRequestStatus now reqId req) >>= \caseExpired r ->if (now ^. _utctDay) > addDays gracePeriod (r ^. billingDate)then throwError (review _Overdue reqId)else fmap (d :) $ rec px dx -- d will be rebilled_ ->rec ps ds -- if paid or unpaid, nothing to do, keep lookingGT ->fmap (d :) $ rec px dsLT ->rec ps dxfindUnbilledDates _ _ ds = pure ds - edit in lib/Aftok/Payments.hs at line 195
forall c m. - edit in lib/Aftok/Payments.hs at line 199
PaymentRequestId -> - replacement in lib/Aftok/Payments.hs at line 201[4.47417]→[4.47417:47482](∅→∅),[5.9236]→[5.4629:4665](∅→∅),[4.47482]→[5.4629:4665](∅→∅),[5.12397]→[5.4629:4665](∅→∅),[5.4665]→[5.5137:5206](∅→∅),[5.5206]→[4.47483:47543](∅→∅)
(PaymentRequestId, PaymentRequest) ->m PaymentRequestStatusgetRequestStatus now (reqid, req) =let ifUnpaid = (if isExpired now req then Expired else Unpaid) reqin maybe ifUnpaid Paid <$> runMaybeT (findPayment reqid)PaymentRequestDetail c ->m (PaymentRequestStatus c)getRequestStatus now reqid req =let ifUnpaid = if isExpired now req then Expired req else Unpaid reqfindPayment' = case paymentRequestCurrency req of(Currency' BTC) -> findPayment BTC reqid(Currency' ZEC) -> findPayment ZEC reqidin maybe ifUnpaid Paid <$> runMaybeT findPayment' - edit in lib/Aftok/Payments.hs at line 210[5.13891]→[5.12574:12639](∅→∅),[5.12639]→[4.47544:47985](∅→∅),[5.28354]→[5.13150:13342](∅→∅),[4.47985]→[5.13150:13342](∅→∅),[5.14318]→[5.13150:13342](∅→∅),[5.13342]→[5.9334:9444](∅→∅),[5.9444]→[5.14551:14564](∅→∅),[5.13434]→[5.14551:14564](∅→∅),[5.14551]→[5.14551:14564](∅→∅),[5.14564]→[4.47986:48223](∅→∅),[5.9824]→[5.14659:14660](∅→∅),[5.13627]→[5.14659:14660](∅→∅),[4.48223]→[5.14659:14660](∅→∅),[5.14659]→[5.14659:14660](∅→∅),[5.14660]→[4.48224:48364](∅→∅),[5.28537]→[5.14736:14769](∅→∅),[4.48364]→[5.14736:14769](∅→∅),[5.14736]→[5.14736:14769](∅→∅),[5.14769]→[5.4860:4874](∅→∅),[5.4874]→[5.14784:14820](∅→∅),[5.14784]→[5.14784:14820](∅→∅),[5.14820]→[4.48365:48438](∅→∅),[4.48438]→[5.14894:14994](∅→∅),[5.14894]→[5.14894:14994](∅→∅),[5.14994]→[4.48439:48598](∅→∅),[5.28697]→[5.4875:4906](∅→∅),[4.48598]→[5.4875:4906](∅→∅),[5.15089]→[5.4875:4906](∅→∅),[5.4906]→[5.28698:28772](∅→∅),[5.28772]→[5.15174:15253](∅→∅),[5.15174]→[5.15174:15253](∅→∅),[5.15253]→[4.48599:48667](∅→∅),[4.48667]→[5.15322:15323](∅→∅),[5.15322]→[5.15322:15323](∅→∅),[5.15323]→[4.48668:48821](∅→∅),[4.48821]→[5.28926:28998](∅→∅),[5.28926]→[5.28926:28998](∅→∅),[5.4965]→[5.15471:15513](∅→∅),[5.28998]→[5.15471:15513](∅→∅),[5.15471]→[5.15471:15513](∅→∅),[5.6086]→[5.28999:29096](∅→∅),[5.29097]→[5.30317:30397](∅→∅),[5.6086]→[5.30317:30397](∅→∅),[5.30397]→[5.3031:3073](∅→∅),[5.3073]→[5.29155:29228](∅→∅),[5.29155]→[5.29155:29228](∅→∅),[5.29228]→[4.48822:48877](∅→∅),[4.48877]→[5.15734:15858](∅→∅),[5.6288]→[5.15734:15858](∅→∅),[5.855]→[5.855:856](∅→∅),[5.856]→[5.15859:15982](∅→∅),[5.15982]→[5.5207:5208](∅→∅)
{- Create the PaymentDetails section of the payment request.-}createPaymentDetails ::( MonadRandom m,MonadReader r m,HasPaymentsConfig r,MonadError e m,AsPaymentError e,MonadDB m) =>-- | payout date (billing date)T.Day ->-- | timestamp of payment request creationC.UTCTime ->-- | user memoMaybe Text ->-- | payment response URLMaybe URI ->-- | merchant payloadMaybe ByteString ->-- | billing informationBillable ->m P.PaymentDetailscreatePaymentDetails payoutDate billingTime memo uri payload b = dopayouts <- getProjectPayouts payoutTime (b ^. project)outputs <- createPayoutsOutputs payoutTime (b ^. amount) payoutslet expiry =(BT.Expiry . T.fromThyme . (billingTime .+^))<$> (b ^. requestExpiryPeriod)cfg <- askpure $B.createPaymentDetails(toNetwork (cfg ^. networkMode) BTC)outputs(T.fromThyme billingTime)expirymemouripayloadwherepayoutTime = T.mkUTCTime payoutDate (fromInteger 0)getProjectPayouts ::(MonadDB m, MonadError e m, AsPaymentError e) =>C.UTCTime ->ProjectId ->m (TL.Payouts (NetworkId, Address))getProjectPayouts ptime pid = doproject' <-let projectOp = FindProject pidin maybe (raiseSubjectNotFound projectOp) pure =<< liftdb projectOpwidx <- liftdb $ ReadWorkIndex pidpure $ TL.payouts (TL.toDepF $ project' ^. depf) ptime widxcreatePayoutsOutputs ::(MonadDB m, MonadError e m, AsPaymentError e) =>C.UTCTime ->BT.Satoshi ->TL.Payouts (NetworkId, Address) ->m [BT.Output]createPayoutsOutputs t amt p =let payoutFractions :: [(TL.CreditTo (NetworkId, Address), BT.Satoshi)]payoutFractions = (_2 %~ outputAmount amt) <$> assocs (p ^. TL._Payouts)in join <$> traverse (uncurry (createOutputs t)) payoutFractionscreateOutputs ::(MonadDB m, MonadError e m, AsPaymentError e) =>C.UTCTime ->TL.CreditTo (NetworkId, Address) ->BT.Satoshi ->m [BT.Output]createOutputs _ (TL.CreditToCurrency (BTC, (PubKeyAddress addr))) amt =pure $ [BT.Output amt (PayPKHash addr)]createOutputs _ (TL.CreditToCurrency (_, other)) _ =throwError $ review _IllegalAddress othercreateOutputs _ (TL.CreditToUser uid) amt = (fmap maybeToList) . runMaybeT $ do(_, addr) <- findUserPaymentAddress uidcase addr ofPubKeyAddress a -> pure $ BT.Output amt (PayPKHash a)other -> throwError $ review _IllegalAddress othercreateOutputs t (TL.CreditToProject pid) amt = dopayouts <- getProjectPayouts t pidcreatePayoutsOutputs t amt payoutsoutputAmount :: BT.Satoshi -> Rational -> BT.SatoshioutputAmount i r = BT.Satoshi . round $ toRational (i ^. satoshi) * r - replacement in lib/Aftok/Payments.hs at line 211[4.48901]→[4.48901:48974](∅→∅),[5.9987]→[5.5302:5367](∅→∅),[4.48974]→[5.5302:5367](∅→∅),[5.5302]→[5.5302:5367](∅→∅),[5.5367]→[5.9988:10076](∅→∅),[5.10076]→[4.48975:49165](∅→∅)
(MonadDB m) => UserId -> SubscriptionId -> C.UTCTime -> m [BillDetail]findPayableRequests uid sid now = dorequests <- liftdb findOpjoin<$> (traverse checkAccess $ filter (not . isExpired now . view _2) requests)wherefindOp = FindUnpaidRequests sidcheckAccess d =if view (_3 . customer) d == uidthen pure [d]else raiseOpForbidden uid (UserNotSubscriber sid) findOp[4.48901](MonadDB m) => UserId -> SubscriptionId -> m [(PaymentRequestId, PT.SomePaymentRequestDetail)]findPayableRequests uid sid = dosubMay <- liftdb (FindSubscription sid)when (maybe True (\s -> s ^. B.customer /= uid) subMay) $void (raiseOpForbidden uid (UserNotSubscriber sid) (FindSubscription sid))findSubscriptionUnpaidRequests sid - replacement in lib/Aftok/Project.hs at line 37
parseInvCode :: Text -> Either String InvitationCodeparseInvCode :: Text -> Either Text InvitationCode - replacement in lib/Aftok/Project.hs at line 39
code <- B64.decode . encodeUtf8 $ tcode <- B64.decodeBase64 . encodeUtf8 $ t - replacement in lib/Aftok/Project.hs at line 45
renderInvCode (InvitationCode bs) = decodeUtf8 $ B64.encode bsrenderInvCode (InvitationCode bs) = B64.encodeBase64 bs - replacement in lib/Aftok/TimeLog.hs at line 10
_CreditToCurrency,_CreditToAccount, - edit in lib/Aftok/TimeLog.hs at line 13
creditToName, - edit in lib/Aftok/TimeLog.hs at line 31
FractionalPayouts, - replacement in lib/Aftok/TimeLog.hs at line 100
eventName (StartWork _) = "start"eventName (StopWork _) = "stop"eventName = \case(StartWork _) -> "start"(StopWork _) -> "stop" - replacement in lib/Aftok/TimeLog.hs at line 105
nameEvent "start" = Just StartWorknameEvent "stop" = Just StopWorknameEvent _ = NothingnameEvent = \case"start" -> Just StartWork"stop" -> Just StopWork_ -> Nothing - replacement in lib/Aftok/TimeLog.hs at line 110
data LogEntry adata LogEntry - replacement in lib/Aftok/TimeLog.hs at line 112
{ _creditTo :: !(CreditTo a),{ _creditTo :: !CreditTo, - replacement in lib/Aftok/TimeLog.hs at line 120
instance Ord a => Ord (LogEntry a) whereinstance Ord LogEntry where - replacement in lib/Aftok/TimeLog.hs at line 133
data EventAmendment adata EventAmendment - replacement in lib/Aftok/TimeLog.hs at line 135
| CreditToChange !ModTime !(CreditTo a)| CreditToChange !ModTime !CreditTo - replacement in lib/Aftok/TimeLog.hs at line 142
newtype Payouts a = Payouts (Map (CreditTo a) Rational)newtype Payouts a = Payouts (Map CreditTo a) - replacement in lib/Aftok/TimeLog.hs at line 146
newtype WorkIndex a = WorkIndex (Map (CreditTo a) (NonEmpty Interval)) deriving (Show, Eq)type FractionalPayouts = Payouts Rationalnewtype WorkIndex = WorkIndex (Map CreditTo (NonEmpty Interval)) deriving (Show, Eq) - replacement in lib/Aftok/TimeLog.hs at line 165
-- - work allocated to each address.payouts :: Ord a => DepF -> C.UTCTime -> WorkIndex a -> Payouts a-- - work allocated to each unique CreditTo.payouts :: DepF -> C.UTCTime -> WorkIndex -> FractionalPayouts - replacement in lib/Aftok/TimeLog.hs at line 174
workIndex :: forall a f. (Ord a, Foldable f) => f (LogEntry a) -> (WorkIndex a)workIndex :: Foldable f => f LogEntry -> WorkIndex - replacement in lib/Aftok/TimeLog.hs at line 179
(CreditTo a) ->CreditTo -> - replacement in lib/Aftok/TimeLog.hs at line 181
Map (CreditTo a) (NonEmpty Interval) ->Map (CreditTo a) (NonEmpty Interval)Map CreditTo (NonEmpty Interval) ->Map CreditTo (NonEmpty Interval) - replacement in lib/Aftok/TimeLog.hs at line 192
type RawIndex a = Map (CreditTo a) [Either LogEvent Interval]type RawIndex = Map CreditTo [Either LogEvent Interval] - replacement in lib/Aftok/TimeLog.hs at line 194
appendLogEntry :: (Ord a) => RawIndex a -> LogEntry a -> RawIndex aappendLogEntry :: RawIndex -> LogEntry -> RawIndex - edit in lib/Aftok/Types.hs at line 1
{-# LANGUAGE DeriveFunctor #-} - replacement in lib/Aftok/Types.hs at line 5
import Aftok.Currency.Zcash (ZAddr)import qualified Aftok.Currency.Zcash.Types as Zcash - edit in lib/Aftok/Types.hs at line 11
import Data.Functor (Functor) - replacement in lib/Aftok/Types.hs at line 29
data AccountRecovery zdata RecoverBy z - replacement in lib/Aftok/Types.hs at line 33
makePrisms ''AccountRecoverymakePrisms ''RecoverBy - replacement in lib/Aftok/Types.hs at line 38
_userAccountRecovery :: !(AccountRecovery ZAddr)_userAccountRecovery :: !(RecoverBy Zcash.Address) - replacement in lib/Aftok/Types.hs at line 47
data CreditTo a-- Identifier for a cryptocurrency account. An account-- is a mapping from cryptocurrency network to address;-- this abstraction permits users to accept payment-- in multiple currencies, or to direct payments in a-- fashion that can change over time.newtype AccountId = AccountId UUID deriving (Show, Eq, Ord)makePrisms ''AccountIddata CreditTo - replacement in lib/Aftok/Types.hs at line 58
CreditToCurrency !aCreditToAccount !AccountId - replacement in lib/Aftok/Types.hs at line 63
deriving (Show, Eq, Ord, Functor)deriving (Show, Eq, Ord) - edit in lib/Aftok/Types.hs at line 67[5.1037]→[5.33719:33807](∅→∅),[5.33807]→[4.55217:55321](∅→∅),[5.43434]→[5.33914:33915](∅→∅),[4.55321]→[5.33914:33915](∅→∅),[5.33914]→[5.33914:33915](∅→∅)
creditToName :: CreditTo a -> TextcreditToName (CreditToCurrency _) = "credit_via_net"creditToName (CreditToUser _) = "credit_to_user"creditToName (CreditToProject _) = "credit_to_project" - edit in lib/Aftok/Util/Http.hs at line 6
import Data.Text (unpack) - replacement in lib/Aftok/Util/Http.hs at line 18
decoded <- either fail pure $ B64.decode b64decoded <- either (fail . unpack) pure $ B64.decodeBase64 b64 - file addition: 2020-11-25_04-22-24_zcash-support.txt[132.1]
Description: (Describe migration here.)Created: 2020-11-25 04:24:09.873312342 UTCDepends: 2020-06-06_03-53-54_add-payment-networks 2017-09-24_22-06-01_billing-templates 2017-06-08_04-37-31_event-metadata-ids 2016-12-31_03-45-17_create-payments 2016-10-14_02-49-36_event-amendments 2016-10-14_02-14-09_create_invitations 2016-10-14_02-11-24_project_companions_invitations 2016-10-13_05-36-55_user-event-logApply: |CREATE TYPE currency_t AS ENUM ('ZEC', 'BTC');ALTER TABLE work_events ALTER COLUMN credit_to_type DROP DEFAULT;ALTER TABLE work_events ALTER COLUMN credit_to_type TYPE VARCHAR(255);ALTER TABLE event_credit_to_amendments ALTER COLUMN credit_to_type TYPE VARCHAR(255);UPDATE work_events SET credit_to_type = 'credit_to_account' WHERE credit_to_type = 'credit_to_address';UPDATE event_credit_to_amendments SET credit_to_type = 'credit_to_account' WHERE credit_to_type = 'credit_to_address';DROP TYPE IF EXISTS credit_to_t;CREATE TYPE credit_to_t AS ENUM ('credit_to_account', 'credit_to_user', 'credit_to_project');ALTER TABLE work_events ALTER COLUMN credit_to_type TYPE credit_to_t USING (credit_to_type::credit_to_t);ALTER TABLE event_credit_to_amendments ALTER COLUMN credit_to_type TYPE credit_to_t USING (credit_to_type::credit_to_t);CREATE TABLE IF NOT EXISTS cryptocurrency_accounts (id uuid primary key default uuid_generate_v4(),user_id uuid references users(id) not null,currency currency_t not null,is_primary bool,zcash_ivk text,zcash_addr text,btc_addr text,UNIQUE (user_id, currency, is_primary),CHECK ((currency = 'BTC' AND btc_addr IS NOT NULL) OR (currency = 'ZEC' AND zcash_ivk IS NOT NULL)));INSERT INTO cryptocurrency_accounts(user_id, currency, btc_addr, is_primary)SELECT DISTINCT id, 'BTC'::currency_t, default_payment_addr, true FROM usersWHERE default_payment_addr IS NOT NULL;INSERT INTO cryptocurrency_accounts(user_id, currency, btc_addr)SELECT DISTINCT user_id, 'BTC'::currency_t, credit_to_address FROM work_eventsWHERE credit_to_address IS NOT NULL;ALTER TABLE work_events ADD COLUMN credit_to_account uuid REFERENCES cryptocurrency_accounts(id);UPDATE work_eventsSET credit_to_account = ca.id, credit_to_type = 'credit_to_account'FROM cryptocurrency_accounts caWHERE ca.user_id = work_events.user_idAND credit_to_address = ca.btc_addr;ALTER TABLE work_events DROP COLUMN credit_to_address;ALTER TABLE event_credit_to_amendments ADD COLUMN credit_to_account uuid REFERENCES cryptocurrency_accounts(id);UPDATE event_credit_to_amendmentsSET credit_to_account = ca.id, credit_to_type = 'credit_to_account'FROM cryptocurrency_accounts caJOIN work_events wON ca.user_id = w.user_idWHERE w.id = event_credit_to_amendments.event_idAND event_credit_to_amendments.credit_to_address = ca.btc_addr;ALTER TABLE event_credit_to_amendments DROP COLUMN credit_to_address;ALTER TABLE billables ADD COLUMN billing_currency currency_t NOT NULL;ALTER TABLE billables ADD COLUMN message text;ALTER TABLE billables ADD COLUMN request_expiry_seconds integer NOT NULL DEFAULT 259200;ALTER TABLE billables ALTER COLUMN billing_amount TYPE bigint;ALTER TABLE payment_requests ALTER COLUMN subscription_id DROP NOT NULL;ALTER TABLE payment_requests ALTER COLUMN url_key DROP NOT NULL;ALTER TABLE payment_requests ADD COLUMN request_json json NOT NULL;ALTER TABLE payment_requests DROP COLUMN request_data;ALTER TABLE payments ADD COLUMN payment_json json NOT NULL;ALTER TABLE payments DROP COLUMN payment_data; - file addition: check_zaddr.sh[5.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"firead -p "Zcash Address: " ZADDRcurl --verbose \${ALLOW_INSECURE} \"https://$AFTOK_HOST/api/validate_zaddr?zaddr=${ZADDR}" - replacement in scripts/create_project.sh at line 35
curl --verbose --insecure --user $USER \--request POST --header "Content-Type: application/json" \curl --verbose \${ALLOW_INSECURE} \--user $USER \--header "Content-Type: application/json" \ - file addition: create_project_billable.sh[5.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"fiif [ -z "${USER}" ]; thenread -p "Username: " USERechofiif [ -z "${PID}" ]; thenread -p "Project UUID: " PIDechofiread -p "Billable Name: " BNAMEread -p "Description: " BDESCwhile [ -z "${RECUR}" ]doread -p "Recurrence Period [A|M|W|O] ((A)nnual, (M)onthly, (W)eekly, (O)ne-time): " RECURcase $RECUR in"A")RECUR="annually"read -p "Recur every ? years: " RECUR_COUNT;;"M")RECUR="monthly"read -p "Recur every ? months: " RECUR_COUNT;;"W")RECUR="weekly"read -p "Recur every ? weeks: " RECUR_COUNT;;"O")RECUR="one-time";;*)echo "$RECUR is not a supported recurrence. Please choose \"A\" \"M\", \"W\" or \"O\""RECUR="";;esacdonewhile [ -z "${CURRENCY}" ]doread -p "Currency [BTC|ZEC]: " CURRENCYcase $CURRENCY in"BTC")read -p "Bill Total (in Satoshis): " AMOUNTbreak;;"ZEC")read -p "Bill Total (in Zatoshis): " AMOUNTbreak;;*)echo "$CURRENCY is not a supported currency. Please choose \"BTC\" or \"ZEC\""CURRENCY="";;esacdoneread -p "Grace Period (days): " GRACE_PERIODread -p "Request Expiry Period (seconds): " REQUEST_EXPIRYBODY=$(cat <<END_BODY{"schemaVersion": "1.0","name": "$BNAME","description": "$BDESC","message": "Thank you for your patronage.","recurrence": { "$RECUR": $RECUR_COUNT },"currency": "$CURRENCY","amount": $AMOUNT,"gracePeriod": $GRACE_PERIOD,"requestExpiryPeriod": $REQUEST_EXPIRY}END_BODY)curl --verbose \${ALLOW_INSECURE} \--user $USER \--header "Content-Type: application/json" \--data "$BODY" \"https://$AFTOK_HOST/api/projects/${PID}/billables" - edit in scripts/create_user.sh at line 15
read -p "BTC Address: " BTC_ADDR - replacement in scripts/create_user.sh at line 16
curl --verbose --insecure \--request POST --header 'Content-Type: application/json' \--data "{\"username\":\"$USER\", \"password\":\"$PASS\", \"email\":\"$EMAIL\", \"btcAddr\":\"$BTC_ADDR\"}" \curl --verbose \${ALLOW_INSECURE} \--header 'Content-Type: application/json' \--data "{\"username\":\"$USER\", \"password\":\"$PASS\", \"recoveryType\": \"email\", \"recoveryEmail\": \"$EMAIL\", \"captchaToken\":\"FAKE\"}" \ - file move: latest_events.sh → get_project.sh
- replacement in scripts/get_project.sh at line 21
curl --verbose --insecure --user $USER \--request GET \"https://$AFTOK_HOST/api/projects/$PID/events?after=2020-01-01T00:00:00Z"[5.252]curl \${ALLOW_INSECURE} \--user $USER \"https://$AFTOK_HOST/api/projects/${PID}" - replacement in scripts/invite.sh at line 24
curl --verbose --insecure --user $USER \--request POST \curl --verbose \${ALLOW_INSECURE} \--user $USER \--header "Content-Type: application/json" \ - file move: list_events.sh → list_project_billables.sh
- replacement in scripts/list_project_billables.sh at line 21
curl --verbose --insecure --user $USER \--request GET \"https://$AFTOK_HOST/api/projects/$PID/events?after=2020-01-01T00:00:00Z"[5.4432]curl --verbose \${ALLOW_INSECURE} \--user $USER \"https://$AFTOK_HOST/api/projects/$PID/billables" - file move: list_intervals.sh → list_project_intervals.sh
- replacement in scripts/list_project_intervals.sh at line 21
curl --verbose --user $USER \--request GET \curl --verbose \${ALLOW_INSECURE} \--user $USER \ - file addition: list_project_payouts.sh[5.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"fiif [ -z "${USER}" ]; thenread -p "Username: " USERechofiif [ -z "${PID}" ]; thenread -p "Project UUID: " PIDechoficurl --verbose \${ALLOW_INSECURE} \--user $USER \"https://$AFTOK_HOST/api/projects/$PID/payouts" - replacement in scripts/list_projects.sh at line 16
curl --insecure --user $USER \curl \${ALLOW_INSECURE} \--user $USER \ - file move: log.sh → list_user_events.sh
- replacement in scripts/list_user_events.sh at line 24
after=$(date -Iseconds --date='1 month ago')after=$(date -Iseconds --date='4 years ago') - edit in scripts/list_user_events.sh at line 26
echo "Retrieving your log entries for project ${PID} after ${after}..." - replacement in scripts/list_user_events.sh at line 29
curl --verbose --insecure --user $USER \--request GET \"https://$AFTOK_HOST/api/projects/$PID/logEntries?after=${after}"[5.284]curl --verbose \${ALLOW_INSECURE} \--user $USER \"https://$AFTOK_HOST/api/user/projects/$PID/events?after=${after}&limit=100" - file addition: list_user_intervals.sh[5.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"fiif [ -z "${USER}" ]; thenread -p "Username: " USERechofiif [ -z "${PID}" ]; thenread -p "Project UUID: " PIDechoficurl --verbose \${ALLOW_INSECURE} \--user $USER \"https://$AFTOK_HOST/api/user/projects/$PID/workIndex?limit=100&before=$(date -Iseconds)" - replacement in scripts/log_end.sh at line 21
curl --verbose --insecure --user $USER \--request POST \curl --verbose \${ALLOW_INSECURE} \--user $USER \--header "Content-Type: application/json" \ - replacement in scripts/log_start.sh at line 21
curl --verbose --insecure --user $USER \--request POST \curl --verbose \${ALLOW_INSECURE} \--user $USER \ - replacement in scripts/login-xhr.sh at line 22
--request POST \${ALLOW_INSECURE} \--header "Content-Type: application/json" \ - replacement in scripts/login.sh at line 16
curl --verbose --user $USER \curl --verbose \${ALLOW_INSECURE} \--user $USER \ - file addition: logout.sh[5.1220]
#!/bin/bashif [ -f ".env" ]; thensource .envfiif [ -z "${AFTOK_HOST}" ]; thenAFTOK_HOST="aftok.com"ficurl --verbose \${ALLOW_INSECURE} \"https://$AFTOK_HOST/api/logout" - edit in server/Aftok/Snaplet/Billing.hs at line 11
import Aftok.Currency (Amount (..), Currency (..))import Aftok.Currency.Bitcoin (Satoshi (..))import Aftok.Currency.Zcash (Zatoshi (..)) - edit in server/Aftok/Snaplet/Billing.hs at line 24
import Bippy.Types (Satoshi (..)) - replacement in server/Aftok/Snaplet/Billing.hs at line 31
parseCreateBillable :: UserId -> ProjectId -> Value -> Parser BillableparseCreateBillable :: UserId -> ProjectId -> Value -> Parser (Billable Amount) - edit in server/Aftok/Snaplet/Billing.hs at line 34
amountParser = \case"ZEC" -> pure (Amount ZEC . Zatoshi)"BTC" -> pure (Amount BTC . Satoshi)c -> fail ("Currency " <> c <> " not recognized.") - replacement in server/Aftok/Snaplet/Billing.hs at line 42
<*> o.: "name"<*> o.: "description"<*> (o .: "name")<*> (o .: "description")<*> (o .: "message") - replacement in server/Aftok/Snaplet/Billing.hs at line 46
<*> (Satoshi <$> o .: "amount")<*> o.: "gracePeriod"<*> (fmap toThyme <$> o .: "requestExpiryPeriod")<*> o.:? "paymentRequestEmailTemplate"<*> o.:? "paymentRequestMemoTemplate"<*> ((o .: "currency" >>= amountParser) <*> o .: "amount")<*> (o .: "gracePeriod")<*> (toThyme <$> o .: "requestExpiryPeriod")<*> (o .:? "paymentRequestEmailTemplate")<*> (o .:? "paymentRequestMemoTemplate") - replacement in server/Aftok/Snaplet/Billing.hs at line 63
billableListHandler :: S.Handler App App [(BillableId, Billable)]billableListHandler :: S.Handler App App [(BillableId, Billable Amount)] - replacement in server/Aftok/Snaplet/Payments.hs at line 5
getPaymentRequestHandler,paymentResponseHandler,getBip70PaymentRequestHandler,bip70PaymentResponseHandler, - replacement in server/Aftok/Snaplet/Payments.hs at line 11
import Aftok.Config as ACimport qualified Aftok.Config as ACimport qualified Aftok.Currency.Bitcoin.Payments as Bitcoin - edit in server/Aftok/Snaplet/Payments.hs at line 15
import Aftok.Payments.Types( NativePayment (..),Payment' (..),PaymentId,nativeRequest,) - edit in server/Aftok/Snaplet/Payments.hs at line 24
import qualified Bippy.Proto as Pimport Control.Exception (try) - replacement in server/Aftok/Snaplet/Payments.hs at line 25
( (.~),(^.),_1,_2,_Left,_Right,preview,( (^.), - edit in server/Aftok/Snaplet/Payments.hs at line 31
import qualified Data.Text.Encoding as T - replacement in server/Aftok/Snaplet/Payments.hs at line 32
import Network.HTTP.Client( HttpException,defaultManagerSettings,managerResponseTimeout,responseTimeoutMicro,)import Network.HTTP.Client.OpenSSLimport Network.Wreq( asValue,defaults,getWith,manager,responseBody,)import OpenSSL.Session (context)-- import Network.HTTP.Client-- ( defaultManagerSettings,-- managerResponseTimeout,-- responseTimeoutMicro,-- )-- import Network.HTTP.Client.OpenSSL-- import Network.Wreq-- ( defaults,-- manager,-- )-- import OpenSSL.Session (context) - replacement in server/Aftok/Snaplet/Payments.hs at line 44
( logError,readRequestBody,( readRequestBody, - replacement in server/Aftok/Snaplet/Payments.hs at line 48
listPayableRequestsHandler :: S.Handler App App [BillDetail]listPayableRequestsHandler :: S.Handler App App [(PaymentRequestId, SomePaymentRequestDetail)] - replacement in server/Aftok/Snaplet/Payments.hs at line 52[5.6264]→[5.6264:6344](∅→∅),[5.6344]→[5.17650:17651](∅→∅),[5.17650]→[5.17650:17651](∅→∅),[5.17651]→[5.6345:6408](∅→∅),[5.6408]→[5.3884:3970](∅→∅)
now <- liftIO $ C.getCurrentTimesnapEval $ findPayableRequests uid sid nowgetPaymentRequestHandler :: S.Handler App App P.PaymentRequestgetPaymentRequestHandler =view (_2 . paymentRequest) <$> getPaymentRequestHandler'snapEval $ findPayableRequests uid sid - replacement in server/Aftok/Snaplet/Payments.hs at line 54
paymentResponseHandler :: AC.BillingConfig -> S.Handler App App PaymentIdpaymentResponseHandler cfg = dobip70PaymentResponseHandler :: AC.BillingConfig -> S.Handler App App PaymentIdbip70PaymentResponseHandler _ = do - replacement in server/Aftok/Snaplet/Payments.hs at line 57
preq <- getPaymentRequestHandler'(prid, preq) <- getBip70PaymentRequestHandler - replacement in server/Aftok/Snaplet/Payments.hs at line 61
pure(pure . Bitcoin.Payment Nothing Nothing Nothing (preq ^. Bitcoin.paymentRequestKey)) - replacement in server/Aftok/Snaplet/Payments.hs at line 64[5.51816]→[4.62475:62753](∅→∅),[4.62753]→[5.52091:52116](∅→∅),[5.2064]→[5.52091:52116](∅→∅),[5.52116]→[4.62754:62870](∅→∅),[5.52228]→[5.5270:5344](∅→∅),[4.62870]→[5.5270:5344](∅→∅),[5.5270]→[5.5270:5344](∅→∅),[5.5344]→[4.62871:63013](∅→∅)
let opts =defaults& manager.~ Left (opensslManagerSettings context)& manager.~ Left( defaultManagerSettings{ managerResponseTimeout = responseTimeoutMicro 10000})exchResp <-liftIO. try @HttpException$ asValue=<< (withOpenSSL $ getWith opts (cfg ^. exchangeRateServiceURI))_ <- traverse (logError . T.encodeUtf8 . show) (preview _Left exchResp)let newPayment =Payment(view _1 preq)pmntnow(preview (_Right . responseBody) exchResp)-- let opts =-- defaults-- & manager-- .~ Left (opensslManagerSettings context)-- & manager-- .~ Left-- ( defaultManagerSettings-- { managerResponseTimeout = responseTimeoutMicro 10000-- }-- )-- exchResp <--- liftIO-- . try @HttpException-- $ asValue-- =<< (withOpenSSL $ getWith opts (cfg ^. exchangeRateServiceURI))-- _ <- traverse (logError . T.encodeUtf8 . show) (preview _Left exchResp)-- (preview (_Right . responseBody) exchResp)let newPayment = Payment (Const prid) now (BitcoinPayment pmnt) - replacement in server/Aftok/Snaplet/Payments.hs at line 84[5.4402]→[4.63014:63098](∅→∅),[5.52489]→[5.4484:4561](∅→∅),[4.63098]→[5.4484:4561](∅→∅),[5.4484]→[5.4484:4561](∅→∅),[5.4561]→[4.63099:63242](∅→∅)
getPaymentRequestHandler' ::S.Handler App App (PaymentRequestId, PaymentRequest)getPaymentRequestHandler' = dopkBytes <- requireParam "paymentRequestKey"pkey <-maybe(snapError 400 $ "parameter paymentRequestKey is formatted incorrectly.")pure(parsePaymentKey pkBytes)getBip70PaymentRequestHandler :: S.Handler App App (PaymentRequestId, Bitcoin.PaymentRequest)getBip70PaymentRequestHandler = do(rid, SomePaymentRequest preq) <- getBip70PaymentRequestHandler'case (preq ^. nativeRequest) ofBip70Request bp -> pure (rid, bp)_ -> snapError 400 $ "Not a BIP-70 bitcoin payment request."getBip70PaymentRequestHandler' ::S.Handler App App (PaymentRequestId, SomePaymentRequestDetail)getBip70PaymentRequestHandler' = dopkey <- Bitcoin.PaymentKey . decodeUtf8 <$> requireParam "paymentRequestKey" - replacement in server/Aftok/Snaplet/Payments.hs at line 98
<> (view _PaymentKey pkey)<> (view Bitcoin._PaymentKey pkey) - replacement in server/Aftok/Snaplet/Payments.hs at line 100
(mapMaybeT snapEval $ findPaymentRequest pkey)[5.52741](mapMaybeT snapEval $ findPaymentRequestByKey pkey) - replacement in server/Aftok/Snaplet/Users.hs at line 16
import Aftok.Currency.Zcash (RPCError, ZAddr, ZValidateAddressErr)import qualified Aftok.Currency.Zcash as Zcashimport Aftok.Currency.Zcash (RPCError, ZValidateAddressErr) - replacement in server/Aftok/Snaplet/Users.hs at line 23
( AccountRecovery (..),Email (..),( Email (..),RecoverBy (..), - replacement in server/Aftok/Snaplet/Users.hs at line 60
{ validateZAddr :: Text -> m (Either (RPCError ZValidateAddressErr) ZAddr),{ validateZAddr :: Text -> m (Either (RPCError ZValidateAddressErr) Zcash.Address), - replacement in server/Aftok/Snaplet/Users.hs at line 67
_userAccountRecovery :: !(AccountRecovery Text)_userAccountRecovery :: !(RecoverBy Text) - replacement in server/Aftok/Snaplet/Users.hs at line 99
(\e -> fail $ "Invitation code was rejected as invalid: " <> e)(\e -> fail $ "Invitation code was rejected as invalid: " <> toString e) - edit in server/Aftok/Snaplet/Users.hs at line 108
deriving (Show) - replacement in server/Aftok/Snaplet/Users.hs at line 122
checkZAddrHandler :: RegisterOps IO -> S.Handler App App ZAddrcheckZAddrHandler :: RegisterOps IO -> S.Handler App App Zcash.Address - replacement in server/Aftok/Snaplet/Users.hs at line 132
Left _ ->snapError 400 "The Z-Address provided for account recovery was invalid."Left err ->snapError 400 $ "The Z-Address provided for account recovery was invalid: " <> show err - replacement in server/Aftok/Snaplet/Users.hs at line 180
( \e ->snapError 400 $ "Invitation code was rejected as invalid: " <> T.pack e)(\e -> snapError 400 $ "Invitation code was rejected as invalid: " <> e) - edit in server/Aftok/Snaplet/WorkLog.hs at line 5
import Aftok.Currency.Bitcoin( NetworkId (..),NetworkMode,toNetwork,) - edit in server/Aftok/Snaplet/WorkLog.hs at line 26
import Haskoin.Address( Address,textToAddr,) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 31
S.Handler App App (EventId, KeyedLogEntry BTCNet)S.Handler App App (EventId, KeyedLogEntry) - edit in server/Aftok/Snaplet/WorkLog.hs at line 35
nmode <- getNetworkMode - replacement in server/Aftok/Snaplet/WorkLog.hs at line 37
case A.eitherDecode requestBody>>= A.parseEither (parseLogEntry nmode uid evCtr) ofcase (A.eitherDecode requestBody >>= A.parseEither (parseLogEntry uid evCtr)) of - edit in server/Aftok/Snaplet/WorkLog.hs at line 53[5.5708]→[5.570:571](∅→∅),[5.35680]→[5.570:571](∅→∅),[5.58697]→[5.570:571](∅→∅),[4.71607]→[5.570:571](∅→∅),[5.570]→[5.570:571](∅→∅),[5.571]→[5.18476:18550](∅→∅),[5.18550]→[5.643:672](∅→∅),[5.643]→[5.643:672](∅→∅),[5.672]→[4.71608:71657](∅→∅),[5.58751]→[5.35681:35743](∅→∅),[4.71657]→[5.35681:35743](∅→∅),[5.12656]→[5.35681:35743](∅→∅),[5.35743]→[4.71658:71692](∅→∅),[5.58788]→[5.4323:4361](∅→∅),[4.71692]→[5.4323:4361](∅→∅),[5.6005]→[5.4323:4361](∅→∅),[5.4361]→[4.71693:71732](∅→∅),[4.71732]→[5.59964:60023](∅→∅),[5.58830]→[5.59964:60023](∅→∅),[5.35805]→[5.10149:10164](∅→∅),[5.60023]→[5.10149:10164](∅→∅),[5.6216]→[5.10149:10164](∅→∅),[5.10164]→[4.71733:72008](∅→∅)
logWorkBTCHandler :: (C.UTCTime -> LogEvent) -> S.Handler App App EventIdlogWorkBTCHandler evCtr = douid <- requireUserIdpid <- requireProjectIdnmode <- getNetworkModelet network = toNetwork nmode BTCaddrBytes <- getParam "btcAddr"requestBody <- readRequestBody 4096timestamp <- liftIO C.getCurrentTimecase fmap decodeUtf8 addrBytes >>= textToAddr network ofNothing ->snapError 400 $"Unable to parse bitcoin address from "<> (show addrBytes)Just addr ->snapEval . createEvent pid uid $LogEntry(CreditToCurrency (BTC, addr))(evCtr timestamp)(A.decode requestBody) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 54
projectWorkIndex :: S.Handler App App (WorkIndex (NetworkId, Address))projectWorkIndex :: S.Handler App App WorkIndex - replacement in server/Aftok/Snaplet/WorkLog.hs at line 60
userEvents :: S.Handler App App [LogEntry (NetworkId, Address)]userEvents :: S.Handler App App [LogEntry] - replacement in server/Aftok/Snaplet/WorkLog.hs at line 73
userWorkIndex :: S.Handler App App (WorkIndex (NetworkId, Address))userWorkIndex :: S.Handler App App WorkIndex - replacement in server/Aftok/Snaplet/WorkLog.hs at line 76
payoutsHandler :: S.Handler App App (Payouts (NetworkId, Address))payoutsHandler :: S.Handler App App FractionalPayouts - edit in server/Aftok/Snaplet/WorkLog.hs at line 91
nmode <- getNetworkMode - replacement in server/Aftok/Snaplet/WorkLog.hs at line 102
(A.parseEither (parseEventAmendment nmode modTime) requestJSON)(A.parseEither (parseEventAmendment modTime) requestJSON) - replacement in server/Aftok/Snaplet/WorkLog.hs at line 104
keyedLogEntryJSON ::NetworkMode -> (EventId, KeyedLogEntry (NetworkId, Address)) -> A.ValuekeyedLogEntryJSON nmode (eid, (pid, uid, ev)) =keyedLogEntryJSON :: (EventId, KeyedLogEntry) -> A.ValuekeyedLogEntryJSON (eid, (pid, uid, ev)) = - replacement in server/Aftok/Snaplet/WorkLog.hs at line 112
<> logEntryFields nmode ev[4.73047]<> logEntryFields ev - edit in server/Main.hs at line 6
import Aftok.Currency.Bitcoin.Payments (_bip70Request) - replacement in server/Main.hs at line 85
serveJSON (workIndexJSON nmode) (method GET projectWorkIndex)serveJSON workIndexJSON $ method GET projectWorkIndex - replacement in server/Main.hs at line 87
serveJSON (payoutsJSON nmode) $ method GET payoutsHandlerserveJSON payoutsJSON $ method GET payoutsHandler - replacement in server/Main.hs at line 89
serveJSON (keyedLogEntryJSON nmode) $ method POST (logWorkHandler f)-- logWorkBTCRoute f =-- serveJSON eventIdJSON $ method POST (logWorkBTCHandler f)serveJSON keyedLogEntryJSON $ method POST (logWorkHandler f) - replacement in server/Main.hs at line 92
serveJSON (fmap $ logEntryJSON nmode) $ method GET userEventsserveJSON (fmap logEntryJSON) $ method GET userEvents - replacement in server/Main.hs at line 94
serveJSON (workIndexJSON nmode) $ method GET userWorkIndexserveJSON workIndexJSON $ method GET userWorkIndex - replacement in server/Main.hs at line 105
payableRequestsRoute =serveJSON billDetailsJSON $ method GET listPayableRequestsHandlergetPaymentRequestRoute =-- payableRequestsRoute =-- serveJSON billDetailsJSON $ method GET listPayableRequestsHandlergetBip70PaymentRequestRoute = - replacement in server/Main.hs at line 111
=<< method GET getPaymentRequestHandlersubmitPaymentRoute =. _bip70Request. snd=<< method GET getBip70PaymentRequestHandlersubmitBip70PaymentRoute = - replacement in server/Main.hs at line 116
method POST (paymentResponseHandler $ cfg ^. billingConfig)method POST (bip70PaymentResponseHandler $ cfg ^. billingConfig) - replacement in server/Main.hs at line 119
("login", loginRoute),("login", xhrLoginRoute),("logout", logoutRoute),("login/check", checkLoginRoute),("register", registerRoute),("validate_zaddr", checkZAddrRoute),( "accept_invitation",acceptInviteRoute),-- , ("projects/:projectId/logStart/:btcAddr" , logWorkBTCRoute StartWork)-- , ("projects/:projectId/logEnd/:btcAddr" , logWorkBTCRoute StopWork)("user/projects/:projectId/logStart", logWorkRoute StartWork),("user/projects/:projectId/logEnd", logWorkRoute StopWork),("user/projects/:projectId/events", userEventsRoute),("user/projects/:projectId/workIndex", userWorkIndexRoute),("projects/:projectId/workIndex", projectWorkIndexRoute),( "projects/:projectId/auctions",auctionCreateRoute), -- <|> auctionListRoute)( "projects/:projectId/billables",billableCreateRoute <|> billableListRoute),("projects/:projectId/payouts", projectPayoutsRoute),("projects/:projectId/invite", inviteRoute),("projects/:projectId", projectRoute),("projects", projectCreateRoute <|> projectListRoute),("login", loginRoute), -- login.sh("login", xhrLoginRoute), -- login_xhr.sh("logout", logoutRoute), -- logout.sh("login/check", checkLoginRoute), -- login.sh("register", registerRoute), -- create_user.sh("validate_zaddr", checkZAddrRoute), -- check_zaddr.sh("accept_invitation", acceptInviteRoute),("user/projects/:projectId/logStart", logWorkRoute StartWork), -- log_start.sh("user/projects/:projectId/logEnd", logWorkRoute StopWork), -- log_end.sh("user/projects/:projectId/events", userEventsRoute), -- list_user_events.sh("user/projects/:projectId/workIndex", userWorkIndexRoute), -- list_user_intervals.sh("projects/:projectId/workIndex", projectWorkIndexRoute), -- list_project_intervals.sh("projects/:projectId/auctions", auctionCreateRoute), -- <|> auctionListRoute)("projects/:projectId/billables", billableCreateRoute <|> billableListRoute), -- create_billable.sh / list_project_billables.sh("projects/:projectId/payouts", projectPayoutsRoute), -- list_project_payouts.sh("projects/:projectId/invite", inviteRoute), -- invite.sh("projects/:projectId", projectRoute), -- get_project.sh("projects", projectCreateRoute <|> projectListRoute), -- create_project.sh, list_projects.sh - replacement in server/Main.hs at line 140
("subscriptions/:subscriptionId/payment_requests", payableRequestsRoute),("pay/:paymentRequestKey", getPaymentRequestRoute <|> submitPaymentRoute),-- ("subscriptions/:subscriptionId/payment_requests", payableRequestsRoute),("pay/btc/:paymentRequestKey", getBip70PaymentRequestRoute <|> submitBip70PaymentRoute), - edit in test/Aftok/TimeLogSpec.hs at line 11
import Aftok.Generators (genUUID) - edit in test/Aftok/TimeLogSpec.hs at line 14
import Aftok.Types (UserId (..)) - edit in test/Aftok/TimeLogSpec.hs at line 21
import Haskoin.Address (Address)import Haskoin.Util.Arbitrary.Address (arbitraryAddress) - replacement in test/Aftok/TimeLogSpec.hs at line 46
genWorkIndex :: Gen (WorkIndex Address)genWorkIndex :: Gen WorkIndex - replacement in test/Aftok/TimeLogSpec.hs at line 48
let recordGen :: Gen (CreditTo Address, L.NonEmpty I.Interval)let recordGen :: Gen (CreditTo, L.NonEmpty I.Interval) - replacement in test/Aftok/TimeLogSpec.hs at line 50
addr <- arbitraryAddressuid <- UserId <$> genUUID - replacement in test/Aftok/TimeLogSpec.hs at line 52
pure (CreditToCurrency addr, ivals)pure (CreditToUser uid, ivals) - replacement in test/Aftok/TimeLogSpec.hs at line 59
testAddrs <- replicateM 3 (generate arbitraryAddress)testUsers <- take 3 <$> sample' (UserId <$> genUUID) - replacement in test/Aftok/TimeLogSpec.hs at line 72
testIntervals :: [(CreditTo Address, I.Interval)]testIntervals :: [(CreditTo, I.Interval)] - replacement in test/Aftok/TimeLogSpec.hs at line 74
addr <- testAddrsuser <- testUsers - replacement in test/Aftok/TimeLogSpec.hs at line 76
pure $ (CreditToCurrency addr, I.interval start' end')testLogEntries :: [LogEntry Address]pure $ (CreditToUser user, I.interval start' end')testLogEntries :: [LogEntry]