Payment request creation.

[?]
Feb 7, 2021, 9:40 PM
V54JCKJX4WL5UGJBCX7VR5O6QKABGUHPLYB4MD2NQQW45OFH5OBAC

Dependencies

  • [2] H2ABVZI2 Add endpoint for payment request creation.
  • [3] SEWTRB6S Implement payment request creation functions.
  • [4] UWMGUJOW Autoformat sources.
  • [5] MU6WOCCJ Update auctions to permit zcash as a funding currency.
  • [6] X3ES7NUA Fine. I'll use ormolu. At least it doesn't break the code.
  • [7] M4PWY5RU Preliminary work to add support for Zcash payments.
  • [8] N6FG4EW6 Working bootstrap modal! Only a little FFI.
  • [9] 27H4DECZ Add billing create API call.
  • [10] KKJSBWO6 Add createPaymentRequestHandler
  • [11] QMRKFEPG Refactor QDB to use a free monad algebra instead.
  • [12] VTZT2ILU Wire up billing navigation.
  • [13] XXJFUZOV Add first revenue date to project payout computation.
  • [14] YBLHJFCN Implement billing modal.
  • [15] NAFJ6RB3 Minor module reorg.
  • [16] U7YAT2ZK Add error reporting to signup form.
  • [17] ANDJ6GEY Add billing component skeleton.
  • [18] 4GOBY5NQ WIP on modals.
  • [19] 3PFXXJTL WIP
  • [20] IR75ZMX3 Return actual events for interval ends, not just timestamps.
  • [21] IPG33FAW Add billing daemon
  • [22] U256ZALI Add captcha check to register route.
  • [23] T2DN23M7 Factor out billing create component.
  • [24] BROSTG5K Beginning of modularization of server.
  • [25] 3HTCTHHU Add halogen-portal dependency and update argonaut.
  • [26] KET5QGQP Add billable list (in-progress)
  • [*] 64C6AWH6 Rename Ananke -> Quixotic, project reboot.
  • [*] GCVQD44V Create amends endpoint, switch to UUID primary keys
  • [*] GMYPBCWE Make docker-compose work.
  • [*] NJNMO72S Add zcash.com submodule and update client to modern halogen.
  • [*] JXG3FCXY Upgrade ps + halogen versions.
  • [*] PPW6ROC5 Render project data
  • [*] QH4UB73N Format with purty.
  • [*] QU5FW67R Add project selection to time tracker.
  • [*] OUR4PAOT Use local dates for display of intervals.
  • [*] DFOBMSAO Initial work on payments API
  • [*] 2XQD6KKK Add invitation logic and clean up DBProg error handling.

Change contents

  • edit in aftok.cabal at line 181
    [29.7248]
    [30.1596]
    , cryptonite
  • edit in client/package-lock.json at line 4340
    [31.174971]
    [31.175547]
    "kjua": {
    "version": "0.9.0",
    "resolved": "https://registry.npmjs.org/kjua/-/kjua-0.9.0.tgz",
    "integrity": "sha512-Wmh5k6hpl+wiYkcEIx0/Ocj1DOxacw/myh/SQ3NbY0RWD4360CXaaAJkdeeV+moqf7fxvACYK95LXQ8vtLWKxA=="
    },
  • edit in client/package.json at line 18
    [33.291]
    [34.251]
    "kjua": "^0.9.0",
  • replacement in client/src/Aftok/Api/Billing.purs at line 6
    [3.89][3.89:160]()
    -- import Control.Monad.Except.Trans (runExceptT, except, withExceptT)
    [3.89]
    [3.105]
    import Control.Monad.Except.Trans (runExceptT)
  • replacement in client/src/Aftok/Api/Billing.purs at line 13
    [3.253][3.255:307]()
    import Data.BigInt (toNumber, fromNumber) as BigInt
    [3.253]
    [3.335]
    import Data.BigInt (toNumber) as BigInt
  • replacement in client/src/Aftok/Api/Billing.purs at line 15
    [3.367][3.367:421]()
    -- import Data.DateTime.Instant (Instant, toDateTime)
    [3.367]
    [3.421]
    import Data.DateTime.Instant (toDateTime)
  • replacement in client/src/Aftok/Api/Billing.purs at line 17
    [3.459][3.308:339](),[3.339][3.459:531](),[3.459][3.459:531]()
    import Foreign.Object (Object)
    -- import Data.Foldable (class Foldable, foldr, foldl, foldMapDefaultR)
    [3.459]
    [3.531]
    import Data.Foldable (class Foldable, foldMapDefaultR)
  • replacement in client/src/Aftok/Api/Billing.purs at line 25
    [3.370][3.63:108](),[3.108][3.842:877](),[3.405][3.842:877](),[3.842][3.842:877]()
    import Data.Traversable (traverse, sequence)
    import Data.UUID (UUID, parseUUID)
    [3.370]
    [3.877]
    import Data.Traversable (class Traversable, traverse, sequence)
    import Data.UUID (UUID, parseUUID, toString)
  • edit in client/src/Aftok/Api/Billing.purs at line 28
    [3.903]
    [3.903]
    import Effect.Class (liftEffect)
  • replacement in client/src/Aftok/Api/Billing.purs at line 31
    [3.956][3.956:990]()
    -- import Foreign.Object (Object)
    [3.956]
    [3.406]
    import Foreign.Object (Object)
  • replacement in client/src/Aftok/Api/Billing.purs at line 39
    [3.312][3.479:497]()
    ( Zatoshi(..) )
    [3.312]
    [3.1086]
    ( Zatoshi )
  • edit in client/src/Aftok/Api/Billing.purs at line 44
    [3.538]
    [3.538]
    , parseDatedResponse
    , parseZatoshi
  • edit in client/src/Aftok/Api/Billing.purs at line 57
    [3.1440]
    [3.54]
    billableIdStr :: BillableId -> String
    billableIdStr (BillableId uuid) = toString uuid
  • replacement in client/src/Aftok/Api/Billing.purs at line 139
    [3.912][3.1441:1612](),[3.1441][3.1441:1612]()
    amount <-
    map Zatoshi
    $ (note (TypeMismatch "Failed to decode as Zatoshi") <<< BigInt.fromNumber)
    =<< (_ .: "zatoshi")
    =<< (bobj .: "amount")
    [3.912]
    [3.1612]
    amount <- parseZatoshi =<< (bobj .: "amount")
  • edit in client/src/Aftok/Api/Billing.purs at line 144
    [3.977]
    [3.1814]
    createBillable :: ProjectId -> Billable -> Aff (Either APIError BillableId)
    createBillable pid billable = do
    let body = RB.json $ billableJSON billable
    response <- post RF.json ("/api/projects/" <> pidStr pid <> "/billables") (Just body)
    parseResponse decodeJson response
    listProjectBillables :: ProjectId -> Aff (Either APIError (Array (Tuple BillableId Billable)))
    listProjectBillables pid = do
    response <- get RF.json ("/api/projects/" <> pidStr pid <> "/billables")
    parseResponse (traverse parseBillableJSON <=< decodeJson) response
  • replacement in client/src/Aftok/Api/Billing.purs at line 170
    [3.2216][3.2216:2220]()
    {
    [3.2216]
    [3.2220]
    { payment_request_id :: String
    , native_request :: {
    zip321_request :: String,
    schemaVersion :: String
    }
    , expires_at :: t
    , total :: Zatoshi
  • edit in client/src/Aftok/Api/Billing.purs at line 178
    [3.2224]
    [3.2224]
    derive instance paymentRequestFunctor :: Functor PaymentRequest'
  • edit in client/src/Aftok/Api/Billing.purs at line 181
    [3.2225]
    [3.2225]
    instance paymentRequestFoldable :: Foldable PaymentRequest' where
    foldr f b (PaymentRequest r) =
    f r.expires_at b
    foldl f b (PaymentRequest r) =
    f b r.expires_at
    foldMap = foldMapDefaultR
    instance paymentRequestTraversable :: Traversable PaymentRequest' where
    traverse f (PaymentRequest r) =
    map (\b -> PaymentRequest (r { expires_at = b })) (f r.expires_at)
    sequence = traverse identity
  • replacement in client/src/Aftok/Api/Billing.purs at line 195
    [3.2273][3.2273:2349](),[3.2349][3.1029:1195](),[3.1195][3.1816:1852]()
    createBillable :: ProjectId -> Billable -> Aff (Either APIError BillableId)
    createBillable pid billable = do
    let body = RB.json $ billableJSON billable
    response <- post RF.json ("/api/projects/" <> pidStr pid <> "/billables") (Just body)
    parseResponse decodeJson response
    [3.2273]
    [3.2396]
    type PaymentRequestMeta =
    { requestName :: String
    , requestDesc :: Maybe String
    }
  • replacement in client/src/Aftok/Api/Billing.purs at line 200
    [3.2397][3.2397:2492](),[3.2492][3.1853:2027]()
    listProjectBillables :: ProjectId -> Aff (Either APIError (Array (Tuple BillableId Billable)))
    listProjectBillables pid = do
    response <- get RF.json ("/api/projects/" <> pidStr pid <> "/billables")
    parseResponse (traverse parseBillableJSON <=< decodeJson) response
    [3.2397]
    [3.2541]
    decodePaymentRequest :: Json -> Either JsonDecodeError (PaymentRequest' String)
    decodePaymentRequest json = do
    obj <- decodeJson json
    payment_request_id <- obj .: "payment_request_id"
    native_request <- obj .: "native_request"
    expires_at <- obj .: "expires_at"
    total <- parseZatoshi =<< (obj .: "total")
    pure $ PaymentRequest { payment_request_id, native_request, expires_at, total }
  • replacement in client/src/Aftok/Api/Billing.purs at line 209
    [3.2542][3.2542:2655]()
    listUnpaidPaymentRequests :: BillableId -> Aff (Either APIError (Array (Tuple PaymentRequestId PaymentRequest)))
    [3.2542]
    [3.2655]
    createPaymentRequest ::
    ProjectId ->
    BillableId ->
    PaymentRequestMeta ->
    Aff (Either APIError PaymentRequest)
    createPaymentRequest pid bid m = do
    let body = RB.json (encodeJson m)
    uri = "/api/projects/" <> pidStr pid <> "/billables/" <> billableIdStr bid <> "/paymentRequests"
    response <- post RF.json uri (Just body)
    liftEffect
    <<< runExceptT
    <<< map (map toDateTime)
    $ parseDatedResponse decodePaymentRequest response
    listUnpaidPaymentRequests ::
    BillableId ->
    Aff (Either APIError (Array (Tuple PaymentRequestId PaymentRequest)))
  • edit in client/src/Aftok/Api/Billing.purs at line 227
    [3.2712]
  • replacement in client/src/Aftok/Api/Json.purs at line 8
    [3.251][3.964:1044]()
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, JsonDecodeError(..))
    [3.251]
    [3.310]
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, JsonDecodeError(..), (.:))
    import Data.BigInt (fromNumber) as BigInt
  • edit in client/src/Aftok/Api/Json.purs at line 21
    [3.2131]
    [3.718]
    import Foreign.Object (Object)
  • edit in client/src/Aftok/Api/Json.purs at line 26
    [3.857]
    [3.857]
    import Aftok.Zcash (Zatoshi(..))
  • edit in client/src/Aftok/Api/Json.purs at line 120
    [3.1696]
    parseZatoshi :: Object Json -> Either JsonDecodeError Zatoshi
    parseZatoshi obj =
    map Zatoshi
    $ (note (TypeMismatch "Failed to decode as Zatoshi") <<< BigInt.fromNumber)
    =<< (obj .: "zatoshi")
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 5
    [3.970][3.970:1011]()
    -- import Data.DateTime (DateTime, date)
    [3.970]
    [3.1011]
    import Control.Monad.State.Class (get)
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 7
    [3.1049][3.1049:1076]()
    import Data.Fixed as Fixed
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 8
    [3.1103][3.1103:1312]()
    import Data.Int as Int
    import Data.Maybe (Maybe(..), maybe)
    import Data.Newtype (unwrap)
    import Data.Number (fromString) as Number
    import Data.Number.Format (toString) as Number
    -- import Data.Unfoldable as U
    [3.1103]
    [3.1312]
    import Data.Maybe (Maybe(..))
    import Data.Tuple (Tuple(..))
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 11
    [3.1363][3.1363:1479]()
    import Data.Time.Duration (Hours(..), Days(..))
    -- import Data.Traversable (traverse)
    import Data.Tuple (Tuple(..))
    [3.1363]
    [3.1479]
    import Data.Traversable (traverse_)
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 13
    [3.1503][3.1503:1574]()
    -- import Effect.Class (liftEffect)
    -- import Effect.Now (nowDateTime)
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 25
    [3.1948][3.1948:1999]()
    , Billable
    , Recurrence(..)
    , createBillable
    [3.1948]
    [3.1999]
    , PaymentRequest'(..)
    , PaymentRequest
    , PaymentRequestMeta
    , createPaymentRequest
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 30
    [3.2003][3.2003:2288]()
    import Aftok.Zcash (ZEC(..), toZatoshi)
    data Field
    = NameField
    | DescField
    | MessageField
    | MonthlyRecurrenceField
    | WeeklyRecurrenceField
    | AmountField
    | GracePeriodField
    | RequestExpiryField
    derive instance fieldEq :: Eq Field
    derive instance fieldOrd :: Ord Field
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 31
    [3.2289][3.2289:2354]()
    data RType
    = RTAnnual
    | RTMonthly
    | RTWeekly
    | RTOneTime
    [3.2289]
    [3.2354]
    data FieldError
    = NameRequired
    | BillableIdNotSet
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 35
    [3.2355][3.2355:2391]()
    derive instance rtypeEq :: Eq RType
    [3.2355]
    [3.2391]
    derive instance fieldEq :: Eq FieldError
    derive instance fieldOrd :: Ord FieldError
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 40
    [3.2433]
    [3.2433]
    , billableId :: Maybe BillableId
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 43
    [3.2490][3.2490:2697]()
    , message :: Maybe String
    , recurrenceType :: RType
    , recurrenceValue :: Maybe Int
    , amount :: Maybe ZEC
    , gracePeriod :: Maybe Days
    , requestExpiry :: Maybe Hours
    , fieldErrors :: Array Field
    [3.2490]
    [3.2697]
    , fieldErrors :: Array FieldError
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 46
    [3.2702][3.2702:2726]()
    data Query a
    = Tell a
    [3.2702]
    [3.2726]
    type Input =
    { projectId :: ProjectId
    , billableId :: Maybe BillableId
    }
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 51
    [3.2727][3.2727:2750]()
    type Input = ProjectId
    [3.2727]
    [3.2750]
    data Query a =
    SetBillableId BillableId a
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 54
    [3.2751][3.2751:2791]()
    type Output = Tuple BillableId Billable
    [3.2751]
    [3.2791]
    type Output = PaymentRequest
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 57
    [3.2804][3.2804:2852]()
    = ProjectChanged ProjectId
    | SetName String
    [3.2804]
    [3.2852]
    = SetName String
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 59
    [3.2871][3.2871:3081]()
    | SetMessage String
    | SetRecurrenceType RType
    | SetRecurrenceMonths String
    | SetRecurrenceWeeks String
    | SetBillingAmount String
    | SetGracePeriod String
    | SetRequestExpiry String
    | SaveBillable
    [3.2871]
    [3.3081]
    | SavePaymentRequest
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 65
    [3.3159][3.3159:3239]()
    = { createBillable :: ProjectId -> Billable -> m (Either APIError BillableId)
    [3.3159]
    [3.3239]
    = { createPaymentRequest ::
    ProjectId ->
    BillableId ->
    PaymentRequestMeta ->
    m (Either APIError PaymentRequest)
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 72
    [3.3246]
    [3.3246]
    modalId :: String
    modalId = "createPaymentRequest"
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 88
    [3.3486][3.3486:3572]()
    { handleAction = eval
    , receive = Just <<< ProjectChanged
    [3.3486]
    [3.3572]
    { handleAction = handleAction
    , handleQuery = handleQuery
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 95
    [3.3659][3.3659:3682]()
    { projectId: input
    [3.3659]
    [3.3682]
    { projectId: input.projectId
    , billableId: input.billableId
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 99
    [3.3731][3.3731:3901]()
    , message : Nothing
    , recurrenceType : RTOneTime
    , recurrenceValue : Nothing
    , amount : Nothing
    , gracePeriod : Nothing
    , requestExpiry : Nothing
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 104
    [3.4012][3.4012:4085]()
    Modals.modalWithSave "createBillable" "Create Billable" SaveBillable
    [3.4012]
    [3.4085]
    Modals.modalWithSave modalId "Create Payment Request" SavePaymentRequest
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 107
    [3.4125][3.4125:4247]()
    [ NameField ]
    [ HH.label
    [ P.for "billableName"]
    [ HH.text "Product Name" ]
    [3.4125]
    [3.4247]
    [ NameRequired ]
    [ HH.label
    [ P.for "requestName"]
    [ HH.text "Request Name" ]
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 113
    [3.4302][3.4302:4466]()
    , P.classes [ C.formControlSm ]
    , P.id_ "billableName"
    , P.placeholder "A name for the product or service you want to bill for"
    [3.4302]
    [3.4466]
    , P.classes [ C.formControl, C.formControlSm ]
    , P.id_ "requestName"
    , P.placeholder "A name for the payment request"
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 118
    [3.4528][3.4528:4973]()
    ]
    , formGroup st
    [ DescField ]
    [ HH.label
    [ P.for "billableDesc"]
    [ HH.text "Product Description" ]
    , HH.input
    [ P.type_ P.InputText
    , P.classes [ C.formControlSm ]
    , P.id_ "billableDesc"
    , P.placeholder "Description of the product or service"
    , E.onValueInput (Just <<< SetDesc)
    ]
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 120
    [3.5008][3.5008:5156]()
    [ MessageField ]
    [ HH.label
    [ P.for "billableMsg"]
    [ HH.text "Message to be included with bill" ]
    [3.5008]
    [3.5156]
    [ ]
    [ HH.label
    [ P.for "requestDesc"]
    [ HH.text "Request Description" ]
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 126
    [3.5213][3.5213:8171]()
    , P.id_ "billableMsg"
    , P.placeholder "Enter your message here"
    , E.onValueInput (Just <<< SetMessage)
    ]
    ]
    , formGroup st
    [MonthlyRecurrenceField, WeeklyRecurrenceField]
    [ HH.label_
    [ HH.input
    ([ P.type_ P.InputRadio
    , P.name "recurType"
    , E.onClick \_ -> Just (SetRecurrenceType RTAnnual)
    ] <> (if st.recurrenceType == RTAnnual then [P.checked true] else []))
    , HH.text " Annual"
    ]
    , HH.label_
    [ HH.input
    ([ P.type_ P.InputRadio
    , P.name "recurType"
    , E.onClick \_ -> Just (SetRecurrenceType RTMonthly)
    ] <> (if st.recurrenceType == RTMonthly then [P.checked true] else []))
    , HH.text " every "
    , HH.input
    [ P.type_ P.InputNumber
    , P.classes [ C.formControlSm ]
    , P.value (if st.recurrenceType == RTMonthly
    then maybe "" show st.recurrenceValue
    else "")
    , P.min 1.0
    , P.max 12.0
    , E.onValueInput (Just <<< SetRecurrenceMonths)
    ]
    , HH.text " Months"
    ]
    , HH.label_
    [ HH.input
    ([ P.type_ P.InputRadio
    , P.name "recurType"
    , E.onClick \_ -> Just (SetRecurrenceType RTWeekly)
    ] <> (if st.recurrenceType == RTWeekly then [P.checked true] else []))
    , HH.text " every "
    , HH.input
    [ P.type_ P.InputNumber
    , P.classes [ C.formControlSm ]
    , P.value (if st.recurrenceType == RTWeekly
    then maybe "" show st.recurrenceValue
    else "")
    , P.min 1.0
    , P.max 12.0
    , E.onValueInput (Just <<< SetRecurrenceWeeks)
    ]
    , HH.text " Weeks"
    ]
    , HH.label_
    [ HH.input
    ([ P.type_ P.InputRadio
    , P.name "recurType"
    , E.onClick \_ -> Just (SetRecurrenceType RTOneTime)
    ] <> (if st.recurrenceType == RTOneTime then [P.checked true] else []))
    , HH.text " One-Time"
    ]
    ]
    , formGroup st
    [AmountField]
    [ HH.label
    [ P.for "billableAmount"]
    [ HH.text "Amount" ]
    , HH.input
    [ P.type_ P.InputNumber
    , P.classes [ C.formControlSm ]
    , P.id_ "billableAmount"
    , P.value (maybe "" (Fixed.toString <<< unwrap) st.amount)
    , P.placeholder "1.0"
    , P.min 0.0
    , E.onValueInput (Just <<< SetBillingAmount)
    [3.5213]
    [3.8171]
    , P.classes [ C.formControl, C.formControlSm ]
    , P.id_ "requestDesc"
    , P.placeholder "Additional descriptive information"
    , E.onValueInput (Just <<< SetDesc)
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 131
    [3.8187][3.8187:8360]()
    , HH.div
    [ P.classes [ ClassName "input-group-append" ] ]
    [ HH.span [ P.classes [ ClassName "input-group-text" ] ] [ HH.text "ZEC" ] ]
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 132
    [3.8372][3.8372:9517]()
    , formGroup st
    [GracePeriodField]
    [ HH.label
    [ P.for "gracePeriod"]
    [ HH.text "Grace Period (Days)" ]
    , HH.input
    [ P.type_ P.InputNumber
    , P.id_ "gracePeriod"
    , P.classes [ C.formControlSm ]
    , P.value (maybe "" (Number.toString <<< unwrap) st.gracePeriod)
    , P.placeholder "Days until a bill is considered overdue"
    , P.min 0.0
    , E.onValueInput (Just <<< SetGracePeriod)
    ]
    ]
    , formGroup st
    [RequestExpiryField]
    [ HH.label
    [ P.for "requestExpiry"]
    [ HH.text "Request Expiry Period (Hours)" ]
    , HH.input
    [ P.type_ P.InputNumber
    , P.id_ "gracePeriod"
    , P.classes [ C.formControlSm ]
    , P.value (maybe "" (Number.toString <<< unwrap) st.requestExpiry)
    , P.placeholder "Hours until a payment request expires"
    , P.min 0.0
    , E.onValueInput (Just <<< SetRequestExpiry)
    ]
    ]
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 135
    [3.9536][3.9536:9653]()
    formGroup :: forall i a. CState -> Array Field -> Array (HH.HTML i a) -> HH.HTML i a
    formGroup st fields body =
    [3.9536]
    [3.9653]
    formGroup :: forall i a. CState -> Array FieldError -> Array (HH.HTML i a) -> HH.HTML i a
    formGroup st fields body =
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 139
    [3.9697][3.9697:9740]()
    (body <> (fieldError st =<< fields))
    [3.9697]
    [3.9740]
    (body <> (fieldError st =<< fields))
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 141
    [3.9741][3.9741:9873]()
    fieldError :: forall i a. CState -> Field -> Array (HH.HTML i a)
    fieldError st field =
    if any (_ == field) st.fieldErrors
    [3.9741]
    [3.9873]
    fieldError :: forall i a. CState -> FieldError -> Array (HH.HTML i a)
    fieldError st field =
    if any (_ == field) st.fieldErrors
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 145
    [3.9899][3.9899:10482]()
    NameField -> err "The name field is required"
    DescField -> err "The description field is required"
    MessageField -> err "The message field is required"
    MonthlyRecurrenceField -> err "You must enter a valid number of months."
    WeeklyRecurrenceField -> err "You must enter a valid number of weeks."
    AmountField -> err "You must enter a valid amount of ZEC"
    GracePeriodField -> err "You must enter a valid number of hours."
    RequestExpiryField -> err "You must enter a valid number of hours."
    [3.9899]
    [3.10482]
    NameRequired -> err "The name field is required"
    BillableIdNotSet -> err "The billable id is missing. Close this dialog and try again."
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 151
    [3.10625][3.10625:13820]()
    eval :: forall slots. Action -> H.HalogenM CState Action slots Output m Unit
    eval = case _ of
    ProjectChanged pid ->
    H.modify_ (_ { projectId = pid })
    SetName name ->
    H.modify_ (_ { name = Just name })
    SetDesc desc ->
    H.modify_ (_ { description = Just desc })
    SetMessage msg ->
    H.modify_ (_ { message = Just msg })
    SetRecurrenceType rtype -> do
    curRecurType <- H.gets _.recurrenceType
    curDuration <- H.gets _.recurrenceValue
    let rdur = case curRecurType of
    RTMonthly | rtype == RTMonthly -> curDuration
    RTWeekly | rtype == RTWeekly -> curDuration
    _ -> Nothing
    H.modify_ (_ { recurrenceType = rtype, recurrenceValue = rdur })
    SetRecurrenceMonths dur ->
    case Int.fromString dur of
    (Just n) -> H.modify_ (_ { recurrenceType = RTMonthly, recurrenceValue = Just n })
    (Nothing) -> pure unit
    SetRecurrenceWeeks dur ->
    case Int.fromString dur of
    (Just n) -> H.modify_ (_ { recurrenceType = RTWeekly, recurrenceValue = Just n })
    (Nothing) -> pure unit
    SetBillingAmount amt ->
    case Fixed.fromString amt of
    (Just zec) -> H.modify_ (_ { amount = Just (ZEC zec) })
    (Nothing) -> pure unit
    SetGracePeriod dur ->
    case Number.fromString dur of
    (Just n) -> H.modify_ (_ { gracePeriod = Just (Days n) })
    (Nothing) -> pure unit
    SetRequestExpiry dur ->
    case Number.fromString dur of
    (Just n) -> H.modify_ (_ { requestExpiry = Just (Hours n) })
    (Nothing) -> pure unit
    SaveBillable -> do
    nameV <- V <<< note [NameField] <$> H.gets (_.name)
    descV <- V <<< note [DescField] <$> H.gets (_.description)
    msgV <- V <<< note [MessageField] <$> H.gets (_.message)
    rtype <- H.gets (_.recurrenceType)
    rvalueV <- case rtype of
    RTAnnual -> pure $ V (Right Annually)
    RTMonthly -> V <<< maybe (Left [MonthlyRecurrenceField]) (Right <<< Monthly) <$> H.gets (_.recurrenceValue)
    RTWeekly -> V <<< maybe (Left [WeeklyRecurrenceField]) (Right <<< Weekly) <$> H.gets (_.recurrenceValue)
    RTOneTime -> pure $ V (Right OneTime)
    zatsV <- V <<< maybe (Left [AmountField]) (Right <<< toZatoshi) <$> H.gets (_.amount)
    gperV <- V <<< note [GracePeriodField] <$> H.gets (_.gracePeriod)
    expiryV <- V <<< note [RequestExpiryField] <$> H.gets (_.requestExpiry)
    let toBillable = { name: _
    , description: _
    , message: _
    , recurrence: _
    , amount: _
    , gracePeriod: _
    , expiryPeriod: _
    }
    reqV :: V (Array Field) Billable
    reqV =
    toBillable <$> nameV
    <*> descV
    <*> msgV
    <*> rvalueV
    <*> zatsV
    <*> gperV
    <*> expiryV
    [3.10625]
    [3.13820]
    handleQuery :: forall slots a. Query a -> H.HalogenM CState Action slots Output m (Maybe a)
    handleQuery = case _ of
    SetBillableId bid a -> do
    H.modify_ (_ { billableId = Just bid })
    pure (Just a)
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 157
    [3.13821][3.13821:14313]()
    case toEither reqV of
    Left errors -> do
    H.modify_ (_ { fieldErrors = errors })
    Right billable -> do
    pid <- H.gets (_.projectId)
    res <- lift $ caps.createBillable pid billable
    case res of
    Right bid -> do
    H.raise (Tuple bid billable)
    lift $ system.toggleModal "createBillable" ModalFFI.HideModal
    Left errs ->
    lift $ system.error (show errs)
    [3.13821]
    [3.14313]
    handleAction :: forall slots. Action -> H.HalogenM CState Action slots Output m Unit
    handleAction = case _ of
    SetName name ->
    H.modify_ (_ { name = Just name })
    SetDesc desc ->
    H.modify_ (_ { description = Just desc })
    SavePaymentRequest -> do
    bidV <- V <<< note [BillableIdNotSet] <$> H.gets (_.billableId)
    nameV <- V <<< note [NameRequired] <$> H.gets (_.name)
    descV <- H.gets (_.description)
    let reqV = { requestName: _, requestDesc: _ } <$> nameV <*> pure descV
    breqV = Tuple <$> bidV <*> reqV
    case toEither breqV of
    Left errors -> do
    H.modify_ (_ { fieldErrors = errors })
    Right (Tuple bid reqMeta) -> do
    pid <- H.gets (_.projectId)
    res <- lift $ caps.createPaymentRequest pid bid reqMeta
    case res of
    Right content -> do
    lift $ system.log "Request created."
    H.raise content
    lift $ system.toggleModal "createPaymentRequest" ModalFFI.HideModal
    Left errs ->
    lift $ system.error (show errs)
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 185
    [3.14362][3.14362:14397]()
    { createBillable: createBillable
    [3.14362]
    [3.14397]
    { createPaymentRequest: createPaymentRequest
  • replacement in client/src/Aftok/Billing/PaymentRequest.purs at line 190
    [3.14452][3.14452:14504]()
    { createBillable: \_ _ -> pure $ Left Forbidden }
    [3.14452]
    [3.14504]
    { createPaymentRequest: \_ _ _ -> pure $ Left Forbidden }
    type QrState = Maybe PaymentRequest
    data QrQuery a
    = QrRender PaymentRequest a
    data QrAction
    = QrInit
    type QrSlot id
    = H.Slot QrQuery Unit id
    qrModalId :: String
    qrModalId = "paymentRequestQR"
    qrcomponent ::
    forall m output.
    Monad m =>
    System m ->
    H.Component HH.HTML QrQuery (Maybe PaymentRequest) output m
    qrcomponent system =
    H.mkComponent
    { initialState
    , render
    , eval:
    H.mkEval
    $ H.defaultEval
    { handleAction = handleAction
    , handleQuery = handleQuery
    , initialize = Just QrInit
    }
    }
    where
    initialState :: Maybe PaymentRequest -> Maybe PaymentRequest
    initialState input = input
    render :: forall slots. QrState -> H.ComponentHTML QrAction slots m
    render st =
    Modals.modalWithClose qrModalId "Payment Request"
    [ HH.div_
    [ HH.div [P.id_ "paymentRequestQRCode"] []
    ]
    ]
    handleQuery :: forall slots a. QrQuery a -> H.HalogenM QrState QrAction slots output m (Maybe a)
    handleQuery = case _ of
    QrRender r a -> do
    lift $ renderQR r
    pure (Just a)
    handleAction :: forall slots. QrAction -> H.HalogenM QrState QrAction slots output m Unit
    handleAction = case _ of
    QrInit -> do
    traverse_ (lift <<< renderQR) =<< get
  • edit in client/src/Aftok/Billing/PaymentRequest.purs at line 247
    [3.14505]
    renderQR :: PaymentRequest -> m Unit
    renderQR (PaymentRequest r) =
    system.renderQR "paymentRequestQRCode" { content: r.native_request.zip321_request }
  • edit in client/src/Aftok/Billing.purs at line 15
    [3.2684]
    [3.3133]
    import DOM.HTML.Indexed.ButtonType (ButtonType(..))
  • replacement in client/src/Aftok/Billing.purs at line 22
    [3.3315][3.2600:2635]()
    -- import Halogen.HTML.Events as E
    [3.3315]
    [3.3315]
    import Halogen.HTML.Events as E
  • edit in client/src/Aftok/Billing.purs at line 39
    [3.3674]
    [3.2636]
    import Aftok.HTML.Classes as C
  • edit in client/src/Aftok/Billing.purs at line 41
    [3.2666]
    [3.2685]
    import Aftok.Modals.ModalFFI as ModalFFI
  • replacement in client/src/Aftok/Billing.purs at line 50
    [3.3822][3.3822:3882]()
    , selectedBillable :: Maybe (Tuple BillableId Billable)
    [3.3822]
    [3.3882]
    , selectedBillable :: Maybe BillableId
  • edit in client/src/Aftok/Billing.purs at line 58
    [3.4614]
    [3.4022]
    | CreatePaymentRequest BillableId
    | PaymentRequestCreated (PaymentRequest)
  • edit in client/src/Aftok/Billing.purs at line 68
    [3.14673]
    [3.4143]
    , showPaymentRequest :: PaymentRequest.QrSlot Unit
  • edit in client/src/Aftok/Billing.purs at line 74
    [3.14738]
    [3.4196]
    _showPaymentRequest = SProxy :: SProxy "showPaymentRequest"
  • edit in client/src/Aftok/Billing.purs at line 78
    [3.4754]
    [3.4313]
    , createPaymentRequest :: PaymentRequest.Capability m
  • replacement in client/src/Aftok/Billing.purs at line 137
    [3.14833][3.14833:14911]()
    [ Modals.modalButton "createBillable" "Create billable" ]
    [3.14833]
    [3.14911]
    [ Modals.modalButton "createBillable" "Create billable" Nothing]
  • replacement in client/src/Aftok/Billing.purs at line 146
    [3.2257][3.2257:2295]()
    _createBillable
    [3.2257]
    [3.2295]
    _createPaymentRequest
  • replacement in client/src/Aftok/Billing.purs at line 148
    [3.2322][3.2322:2433]()
    (Create.component system caps.createBillable)
    (unwrap p).projectId
    [3.2322]
    [3.2433]
    (PaymentRequest.component system caps.createPaymentRequest)
    { projectId: (unwrap p).projectId, billableId: st.selectedBillable }
  • replacement in client/src/Aftok/Billing.purs at line 151
    [3.2463][3.2463:2512]()
    (Just <<< BillableCreated)
    [3.2463]
    [3.5078]
    (Just <<< PaymentRequestCreated)
    , system.portal
    _showPaymentRequest
    unit
    (PaymentRequest.qrcomponent system)
    Nothing
    Nothing
    (const Nothing)
  • edit in client/src/Aftok/Billing.purs at line 182
    [3.3546][3.3546:3551]()
  • replacement in client/src/Aftok/Billing.purs at line 192
    [3.15334][3.15334:15414]()
    [ Modals.modalButton "createPaymentRequest" "New Payment Request" ]
    [3.15334]
    [3.3801]
    [ HH.button
    [ P.classes [ C.btn, C.btnPrimary ]
    , P.type_ ButtonButton
    , E.onClick (\_ -> Just $ CreatePaymentRequest bid)
    ]
    [ HH.text "New Payment Request" ]
    ]
  • edit in client/src/Aftok/Billing.purs at line 203
    [3.3964][3.3964:3965]()
  • edit in client/src/Aftok/Billing.purs at line 222
    [3.15544]
    [3.4173]
    CreatePaymentRequest bid -> do
    H.modify_ (_ { selectedBillable = Just bid })
    _ <- H.query _createPaymentRequest unit $ H.tell (PaymentRequest.SetBillableId bid)
    lift $ system.toggleModal PaymentRequest.modalId ModalFFI.ShowModal
    PaymentRequestCreated req -> do
    lift $ system.log "Created payment request, closing modal."
    lift $ system.toggleModal PaymentRequest.modalId ModalFFI.HideModal
    lift $ system.log "About to show QR code modal"
    lift $ system.toggleModal PaymentRequest.qrModalId ModalFFI.ShowModal
    _ <- H.query _showPaymentRequest unit $ H.tell (PaymentRequest.QrRender req)
    pure unit
  • edit in client/src/Aftok/Billing.purs at line 246
    [3.19213]
    [3.7177]
    , createPaymentRequest: PaymentRequest.apiCapability
  • replacement in client/src/Aftok/Billing.purs at line 253
    [3.7336][3.5267:5337]()
    { createBillable: { createBillable: \_ _ -> pure $ Left Forbidden }
    [3.7336]
    [3.7386]
    { createBillable: Create.mockCapability
    , createPaymentRequest: PaymentRequest.mockCapability
  • file addition: KjuaQR.js (----------)
    [3.3158]
    exports.renderQRInternal = selector => content => () => {
    $('#' + selector).kjua(content)
    }
  • file addition: KjuaQR.purs (----------)
    [3.3158]
    module Aftok.HTML.KjuaQR
    ( QRType(..)
    , QROpts
    , renderQR
    )
    where
    import Prelude
    import Effect (Effect)
    data QRType
    = Canvas
    | Image
    | SVG
    data ErrorCorrection = L | M | Q | H
    renderQR :: String -> QROpts -> Effect Unit
    renderQR = renderQRInternal
    type QROpts =
    { content :: String
    }
    -- -- render method: 'canvas', 'image' or 'svg'
    -- render :: QRType,
    -- -- render pixel-perfect lines
    -- crisp :: Boolean,
    -- -- minimum version: 1..40
    -- minVersion :: Int,
    -- -- error correction level: 'L', 'M', 'Q' or 'H'
    -- ecLevel :: ErrorCorrection,
    -- -- size in pixel: 200
    -- size :: Int,
    -- -- pixel-ratio, null for devicePixelRatio
    -- -- ratio :: null,
    --
    -- --code color: '#333',
    -- fill :: String -- hack, fine for now
    -- -- background color '#fff'
    -- back :: String,
    --
    -- -- content
    -- text :: String,
    --
    -- -- roundend corners in pc: 0..100
    -- rounded: Int,
    --
    -- -- quiet zone in modules: 0
    -- quiet: Int,
    --
    -- -- modes: 'plain', 'label' or 'image'
    -- mode: 'plain',
    --
    -- -- label/image size and pos in pc: 0..100
    -- mSize: 30,
    -- mPosX: 50,
    -- mPosY: 50,
    --
    -- -- label
    -- label: 'no label',
    -- fontname: 'sans',
    -- fontcolor: '#333',
    --
    -- -- image element
    -- image: null
    --
    -- type QROptsInternal =
    -- {
    -- -- render method: 'canvas', 'image' or 'svg'
    -- render :: String,
    --
    -- -- render pixel-perfect lines
    -- crisp :: Boolean,
    --
    -- -- minimum version: 1..40
    -- minVersion :: Int
    --
    -- -- error correction level: 'L', 'M', 'Q' or 'H'
    -- ecLevel: 'L',
    --
    -- -- size in pixel
    -- size: 200,
    --
    -- -- pixel-ratio, null for devicePixelRatio
    -- ratio: null,
    --
    -- -- code color
    -- fill: '#333',
    --
    -- -- background color
    -- back: '#fff',
    --
    -- -- content
    -- text: 'no text',
    --
    -- -- roundend corners in pc: 0..100
    -- rounded: 0,
    --
    -- -- quiet zone in modules
    -- quiet: 0,
    --
    -- -- modes: 'plain', 'label' or 'image'
    -- mode: 'plain',
    --
    -- -- label/image size and pos in pc: 0..100
    -- mSize: 30,
    -- mPosX: 50,
    -- mPosY: 50,
    --
    -- -- label
    -- label: 'no label',
    -- fontname: 'sans',
    -- fontcolor: '#333',
    --
    -- -- image element
    -- image: null
    --
    --
    -- }
    foreign import renderQRInternal :: String -> QROpts -> Effect Unit
  • replacement in client/src/Aftok/Modals.purs at line 15
    [3.4809][3.4809:4894]()
    modalButton :: forall w i. String -> String -> HH.HTML w i
    modalButton target text =
    [3.4809]
    [3.4894]
    modalButton :: forall action slots m. String -> String -> Maybe action -> H.ComponentHTML action slots m
    modalButton target text action =
  • edit in client/src/Aftok/Modals.purs at line 22
    [3.5037]
    [3.5037]
    , E.onClick (\_ -> action)
  • replacement in client/src/Aftok/Modals.purs at line 26
    [3.5065][3.19349:19367](),[3.19367][3.3280:3332](),[3.3280][3.3280:3332]()
    modalWithSave ::
    forall action slots m.
    String ->
    String ->
    [3.5065]
    [3.3332]
    modalWithSave ::
    forall action slots m.
    String ->
    String ->
  • replacement in client/src/Aftok/Modals.purs at line 31
    [3.3344][3.3344:3389]()
    Array (H.ComponentHTML action slots m) ->
    [3.3344]
    [3.3389]
    Array (H.ComponentHTML action slots m) ->
  • edit in client/src/Aftok/Modals.purs at line 78
    [3.6393]
    modalWithClose ::
    forall i w.
    String ->
    String ->
    Array (HH.HTML i w) ->
    HH.HTML i w
    modalWithClose modalId title contents =
    HH.div
    [ P.classes [ C.modal ]
    , P.id_ modalId
    , P.tabIndex (negate 1)
    , ARIA.role "dialog"
    , ARIA.labelledBy (modalId <> "Title")
    , ARIA.hidden "true"
    ]
    [ HH.div
    [ P.classes [C.modalDialog], ARIA.role "document" ]
    [ HH.div
    [ P.classes [C.modalContent] ]
    [ HH.div
    [ P.classes [C.modalHeader] ]
    [ HH.h5 [P.classes [C.modalTitle], P.id_ (modalId <>"Title") ] [HH.text title]
    , HH.button
    [ P.classes [ C.close ]
    , AP.dataDismiss "modal"
    , ARIA.label "Close"
    , P.type_ ButtonButton
    ]
    [ HH.span [ARIA.hidden "true"] [HH.text "×"]]
    ]
    , HH.div
    [ P.classes [C.modalBody] ]
    contents
    , HH.div
    [ P.classes [C.modalFooter] ]
    [ HH.button
    [ P.type_ ButtonButton
    , P.classes [ C.btn, C.btnSecondary]
    , AP.dataDismiss "modal"
    ]
    [ HH.text "Close" ]
    ]
    ]
    ]
    ]
  • edit in client/src/Aftok/Types.purs at line 29
    [3.3753]
    [35.7524]
    import Aftok.HTML.KjuaQR as KjuaQR
  • edit in client/src/Aftok/Types.purs at line 54
    [3.4228]
    [34.36602]
    , renderQR :: String -> KjuaQR.QROpts -> m Unit
  • edit in client/src/Aftok/Types.purs at line 69
    [3.4389]
    [36.7336]
    , renderQR: \i opts -> liftEffect (KjuaQR.renderQR i opts)
  • edit in lib/Aftok/Config.hs at line 10
    [2.1356]
    [2.1356]
    import Aftok.Currency.Zcash.Types (Memo(..))
  • edit in lib/Aftok/Config.hs at line 15
    [2.1499]
    [3.9592]
    import Aftok.Types (AccountId)
  • edit in lib/Aftok/Config.hs at line 21
    [2.1532]
    [3.9673]
    )
    import Crypto.Random.Types
    ( MonadRandom,
    getRandomBytes,
  • edit in lib/Aftok/Config.hs at line 41
    [3.10015]
    [3.10015]
    import Haskoin.Address (encodeBase58Check)
  • replacement in lib/Aftok/Config.hs at line 137
    [2.3016][2.3016:3088]()
    toPaymentsConfig :: MonadDB m => BillingConfig -> IO (PaymentsConfig m)
    [2.3016]
    [2.3088]
    toPaymentsConfig :: (MonadRandom m, MonadDB m) => BillingConfig -> IO (PaymentsConfig m)
  • replacement in lib/Aftok/Config.hs at line 140
    [2.3173][2.3173:3273]()
    let btcOps = Bitcoin.BillingOps _memoGen (_uriGen $ cfg ^. bitcoinConfig . bip70Host) _payloadGen
    [2.3173]
    [2.3273]
    let btcOps = Bitcoin.BillingOps _btcMemoGen (_uriGen $ cfg ^. bitcoinConfig . bip70Host) _payloadGen
  • edit in lib/Aftok/Config.hs at line 144
    [2.3369]
    [2.3369]
    _zcashBillingOps = _zcashMemoGen,
  • replacement in lib/Aftok/Config.hs at line 148
    [2.3420][2.3420:3432]()
    _memoGen ::
    [2.3420]
    [2.3432]
    _btcMemoGen ::
  • replacement in lib/Aftok/Config.hs at line 154
    [2.3510][2.3510:3553]()
    _memoGen bill billingDate requestTime = do
    [2.3510]
    [2.3553]
    _btcMemoGen bill billingDate requestTime = do
  • edit in lib/Aftok/Config.hs at line 168
    [2.4001]
    [2.4001]
    _zcashMemoGen ::
    (MonadRandom m, MonadDB m) =>
    B.Billable Zatoshi ->
    Day ->
    UTCTime ->
    AccountId ->
    m (Maybe Memo)
    _zcashMemoGen _ _ _ _ = do
    pkey <- encodeBase58Check <$> getRandomBytes 32
    -- for now
    pure $ Just (Memo $ encodeUtf8 pkey)
  • edit in lib/Aftok/Config.hs at line 200
    [2.4481]
  • replacement in lib/Aftok/Currency/Zcash/Zip321.hs at line 63
    [3.11378][3.11378:11443]()
    paramIndex = maybe "" (\i -> pack (printf ".%d" i)) . find (> 0)
    [3.11378]
    [3.11443]
    paramIndex = \case
    Just i | i > 0 -> pack (printf ".%d" i)
    _ -> ""
  • replacement in lib/Aftok/Currency/Zcash/Zip321.hs at line 100
    [3.12442][3.12442:12520]()
    intercalate "&" . toList . itemParams <$> zip (Just <$> fromList [1 ..]) xs
    [3.12442]
    [3.12520]
    intercalate "&" . toList . itemParams <$> zip (Just <$> fromList [0 ..]) xs
  • replacement in lib/Aftok/Database/PostgreSQL/Users.hs at line 87
    [3.59107][3.59107:59173]()
    findUserPaymentAddress :: UserId -> Currency a c -> DBM (Maybe a)
    [3.59107]
    [3.59173]
    findUserPaymentAddress :: UserId -> Currency a c -> DBM (Maybe (AccountId, a))
  • replacement in lib/Aftok/Database/PostgreSQL/Users.hs at line 93
    [3.59276][3.59276:59371]()
    (bitcoinAddressParser mode)
    [sql| SELECT btc_addr FROM cryptocurrency_accounts
    [3.59276]
    [3.59371]
    ((,) <$> idParser AccountId <*> bitcoinAddressParser mode)
    [sql| SELECT id, btc_addr FROM cryptocurrency_accounts
  • replacement in lib/Aftok/Database/PostgreSQL/Users.hs at line 102
    [3.59546][3.59546:59636]()
    (zcashAddressParser)
    [sql| SELECT zcash_addr FROM cryptocurrency_accounts
    [3.59546]
    [3.59636]
    ((,) <$> idParser AccountId <*> zcashAddressParser)
    [sql| SELECT id, zcash_addr FROM cryptocurrency_accounts
  • replacement in lib/Aftok/Database.hs at line 79
    [3.27125][3.64356:64425]()
    FindUserPaymentAddress :: UserId -> Currency a c -> DBOp (Maybe a)
    [3.27125]
    [3.64425]
    FindUserPaymentAddress :: UserId -> Currency a c -> DBOp (Maybe (AccountId, a))
  • replacement in lib/Aftok/Database.hs at line 170
    [3.2786][3.66083:66161]()
    findUserPaymentAddress :: (MonadDB m) => UserId -> Currency a c -> MaybeT m a
    [3.2786]
    [3.66161]
    findUserPaymentAddress :: (MonadDB m) => UserId -> Currency a c -> MaybeT m (AccountId, a)
  • replacement in lib/Aftok/Database.hs at line 173
    [3.66240][3.66240:66408]()
    findAccountPaymentAddress :: (MonadDB m) => AccountId -> Currency a c -> MaybeT m a
    findAccountPaymentAddress uid n = MaybeT . liftdb $ FindAccountPaymentAddress uid n
    [3.66240]
    [3.7626]
    findAccountPaymentAddress :: (MonadDB m) => AccountId -> Currency a c -> MaybeT m (AccountId, a)
    findAccountPaymentAddress aid n = fmap (aid,) . MaybeT . liftdb $ FindAccountPaymentAddress aid n
  • edit in lib/Aftok/Payments/Bitcoin.hs at line 27
    [3.72800]
    [3.72800]
    import Aftok.Types (AccountId)
  • replacement in lib/Aftok/Payments/Bitcoin.hs at line 156
    [3.76728][3.76728:76825]()
    toOutput :: (Address, Satoshi) -> Either PaymentError Output
    toOutput (addr, amt) = case addr of
    [3.76728]
    [3.76825]
    toOutput :: ((AccountId, Address), Satoshi) -> Either PaymentError Output
    toOutput ((_, addr), amt) = case addr of
  • replacement in lib/Aftok/Payments/Util.hs at line 20
    [3.80100][3.80100:80131]()
    import Aftok.Types (ProjectId)
    [3.80100]
    [3.80131]
    import Aftok.Types (ProjectId, AccountId)
  • replacement in lib/Aftok/Payments/Util.hs at line 53
    [3.21828][3.81168:81210](),[3.81168][3.81168:81210]()
    ExceptT PaymentRequestError m (Map a c)
    [3.21828]
    [3.81210]
    ExceptT PaymentRequestError m (Map (AccountId, a) c)
  • replacement in lib/Aftok/Payments/Util.hs at line 76
    [3.82101][3.82101:82142]()
    ExceptT PaymentRequestError m [(a, c)]
    [3.82101]
    [3.82142]
    ExceptT PaymentRequestError m [((AccountId, a), c)]
  • edit in lib/Aftok/Payments/Zcash.hs at line 13
    [3.82853]
    [3.82853]
    import Aftok.Currency.Zcash.Types (Memo(..))
  • edit in lib/Aftok/Payments/Zcash.hs at line 18
    [3.83087]
    [3.83087]
    import Aftok.Types (AccountId)
  • edit in lib/Aftok/Payments/Zcash.hs at line 32
    [3.83356]
    [3.83356]
    type MemoGen m = Billable Zatoshi -> C.Day -> C.UTCTime -> AccountId -> m (Maybe Memo)
  • edit in lib/Aftok/Payments/Zcash.hs at line 36
    [3.83387]
    [3.83387]
    MemoGen m ->
  • replacement in lib/Aftok/Payments/Zcash.hs at line 39
    [3.83466][3.83466:83483]()
    paymentOps cfg =
    [3.83466]
    [3.83483]
    paymentOps memoGen cfg =
  • replacement in lib/Aftok/Payments/Zcash.hs at line 41
    [3.83499][3.83499:83585]()
    { PT.newPaymentRequest = ((fmap PT.Zip321Request .) .) . zip321PaymentRequest cfg
    [3.83499]
    [3.83585]
    { PT.newPaymentRequest = ((fmap PT.Zip321Request .) .) . zip321PaymentRequest cfg memoGen
  • edit in lib/Aftok/Payments/Zcash.hs at line 44
    [3.83592]
    [3.83592]
    -- TODO: Return a richer type that can include per-item uniqueness that can
    -- be used for tracking payments. A payment request, though it's a request for
    -- a single transaction, is really a request for multiple payments that we need
    -- to be able to verify individually since they'll be independent notes.
    --
    -- However, this doesn't really become important until we start generating addresses
    -- from Zcash IVKs, so it's not essential for right now.
  • edit in lib/Aftok/Payments/Zcash.hs at line 52
    [3.83616]
    [3.83616]
    forall m.
  • edit in lib/Aftok/Payments/Zcash.hs at line 55
    [3.83653]
    [3.83653]
    -- | generator for the memo to be associated with each item
    MemoGen m ->
  • replacement in lib/Aftok/Payments/Zcash.hs at line 64
    [3.83858][3.83858:83910]()
    zip321PaymentRequest cfg billable billingDay _ = do
    [3.83858]
    [3.83910]
    zip321PaymentRequest cfg memoGen billable billingDay billTime = do
  • replacement in lib/Aftok/Payments/Zcash.hs at line 69
    [3.84185][3.84185:84278]()
    PaymentRequest <$> (tryJust PT.NoRecipients $ nonEmpty (toPaymentItem <$> assocs payouts))
    [3.84185]
    [3.84278]
    itemsMay <- lift $ nonEmpty <$> traverse toPaymentItem (assocs payouts)
    PaymentRequest <$> tryJust PT.NoRecipients itemsMay
  • replacement in lib/Aftok/Payments/Zcash.hs at line 72
    [3.84286][3.84286:84386]()
    toPaymentItem :: (Address, Zatoshi) -> PaymentItem
    toPaymentItem (a, z) =
    PaymentItem
    [3.84286]
    [3.84386]
    toPaymentItem :: ((AccountId, Address), Zatoshi) -> m PaymentItem
    toPaymentItem ((aid, a), z) = do
    memo <- memoGen billable billingDay billTime aid
    pure $ PaymentItem
  • replacement in lib/Aftok/Payments/Zcash.hs at line 80
    [3.84507][3.84507:84597]()
    _memo = Nothing, -- Just . Memo $ toASCIIBytes (reqid ^. PT._PaymentRequestId),
    [3.84507]
    [3.84597]
    _memo = memo,
  • edit in lib/Aftok/Payments.hs at line 79
    [3.85872]
    [3.85872]
    _zcashBillingOps :: !(Zcash.MemoGen m),
  • replacement in lib/Aftok/Payments.hs at line 136
    [3.87731][3.87731:87801]()
    let ops = Zcash.paymentOps (cfg ^. zcashPaymentsConfig)
    [3.87731]
    [3.87801]
    let ops = Zcash.paymentOps (cfg ^. zcashBillingOps) (cfg ^. zcashPaymentsConfig)
  • edit in server/Aftok/Snaplet/Billing.hs at line 26
    [2.7516]
    [3.60150]
    import Aftok.Database.PostgreSQL (QDBM)
  • edit in server/Aftok/Snaplet/Billing.hs at line 29
    [3.60187][2.7517:7530]()
    MonadDB,
  • edit in server/Aftok/Snaplet/Billing.hs at line 50
    [3.16768]
    [3.16768]
    zcashBillingOps
  • edit in server/Aftok/Snaplet/Billing.hs at line 68
    [3.16978]
    [3.16978]
    qdbmEval
  • edit in server/Aftok/Snaplet/Billing.hs at line 73
    [2.7860]
    [2.7860]
    import Control.Monad.Trans.Except (mapExceptT)
  • replacement in server/Aftok/Snaplet/Billing.hs at line 132
    [3.17355][2.7923:7938](),[2.7938][3.17372:17394](),[3.17372][3.17372:17394]()
    MonadDB m =>
    PaymentsConfig m ->
    [3.17355]
    [3.17394]
    PaymentsConfig QDBM ->
  • replacement in server/Aftok/Snaplet/Billing.hs at line 147
    [3.17962][3.17962:18130]()
    let ops = Zcash.paymentOps (cfg ^. zcashPaymentsConfig)
    res <- snapEval . runExceptT $ createPaymentRequest ops now bid (b & B.amount .~ v) billDay
    [3.17962]
    [3.18130]
    let ops = Zcash.paymentOps (cfg ^. zcashBillingOps) (cfg ^. zcashPaymentsConfig)
    res <- runExceptT . mapExceptT qdbmEval $ createPaymentRequest ops now bid (b & B.amount .~ v) billDay
  • replacement in server/Aftok/Snaplet/Billing.hs at line 175
    [2.8177][2.8177:8252]()
    obj $ ["payment_request_id" .= (rid ^. _PaymentRequestId)] <> fields req
    [2.8177]
    [2.8252]
    v1 . obj $
    ["payment_request_id" .= (rid ^. _PaymentRequestId)] <> fields req
  • replacement in server/Aftok/Snaplet.hs at line 13
    [3.73269][3.73269:73312]()
    import Aftok.Database.PostgreSQL (runQDBM)
    [3.73269]
    [3.73312]
    import Aftok.Database.PostgreSQL (QDBM, runQDBM)
  • edit in server/Aftok/Snaplet.hs at line 84
    [3.74512]
    [38.11945]
    either handleDBError pure e
    qdbmEval ::
    (MonadSnap m, HasPostgres m, HasNetworkMode m) => QDBM a -> m a
    qdbmEval p = do
    let handleDBError (OpForbidden (UserId uid) reason) =
    snapError 403 $ show reason <> " (User " <> show uid <> ")"
    handleDBError (SubjectNotFound) =
    snapError
    404
    "The subject of the requested operation could not be found."
    handleDBError (EventStorageFailed) =
    snapError 500 "The event submitted could not be saved to the log."
    nmode <- getNetworkMode
    e <- liftPG $
    \conn -> liftIO $ runExceptT (runQDBM nmode conn p)