H2ABVZI2NFTERQMJ2Z7WGMRNORV3OQQWCCFEN6YO5GAUT2ONM2MAC
KKJSBWO6RNORAPTJPCCUJJNVI2OYTGLQKB3XJGOASH43GNTJBMKAC
HMDM3B557TO5RYP2IGFFC2C2VN6HYZTDQ47CJY2O37BW55DSMFZAC
64C6AWH66FDKU6UE6Z6JPX2J2GBM2JOPTH2GL6LHKAIUBGNGDZ5AC
DFOBMSAODB3NKW37B272ZXA2ML5HMIH3N3C4GT2DPEQS7ZFK4SNAC
IPG33FAWXGEQ2PO6OXRT2PWWXHRNMPVUKKADL6UKKN5GD2CNZ25AC
4B66XH43UYRVNTX57ORJ7U6IJTRFKSUS6IJ3CXVODMEF7NA7UHVQC
QO4NFWIYHF45PF7BA4IYGVZZ7CVZDHIV2427MQ6NXWHLIGBHBQCAC
MJDIMD5BQEBC265AQAGYE2K6EHHS7ZMZY3I6WE5MCDSTA2E2VY7AC
M4PWY5RUV72AEDCNC4O7UKBPHBIACR4354YTSC3SUZGWFV5UBJBQC
X3ES7NUA42D2BF7CQDDKXM5CLMVCYA3H5YU5KXLPTGDBFPE2LNVAC
B6HWAPDPXIWH7CHK5VLMWLL6EQN6NOFZEFYO47BPUY2ZO4SL7VDAC
EFSXYZPOGA5M4DN65IEIDO7JK6U34DMQELAPHOIL2UAT6HRC66BAC
ENNZIQJG4XJ62QCNRMLNAXN7ICTPCHQFZTURX6QSUYYWNADFJHXQC
W35DDBFYF6Z4ZPCFEO5RPAONZLYCSNTXUSTS6FIUUVZHCI6Q7GHAC
MU6WOCCJQWG4A5NLD3GBFATCE3SRE3QQCYXYH6WIKSGLHQOOBVRAC
2WOOGXDHVQ6L2MQYUTLJ6H6FVSQNJN6SMJL5DG7HAHFYPJLRT2SAC
PBD7LZYQHXAA3KLH2ZUX5GW4UFML6BQ32KXZF4KZ6OYFASUYFJ5QC
4U7F3CPIDTK6JSEDMNMHVKSR7HOQDLZQD2PPVMDLHO5SFSIMUXZAC
V2VDN77HCSRYYWXDJJ2XOVHV4P6PVWNJZLXZ7JUYPQEZQIH5BZ3QC
GMYPBCWEB6NKURRILAHR3TJUKDOGR2ZMK5I6MS6P5G2LAGH36P3QC
6L5BK5EHPAOQX3JCKUJ273UDNAC23LPQL4HIJGM4AV3P3QK5OKIQC
MJ6R42RCK2ASXAJ6QXDPMAW56RBOJ4F4HI2LFIV3KXFIKWYMQK3QC
4R7XIYK3BP664CO3YJ2VM64ES2JYN27UTQG5KS34OTEPAIODSZLQC
NEDDHXUK3GNFMOFO3KLU7NRIHCTYNWBT3D6HTKZAOXFDE6HMDZ6AC
2XQD6KKKD6QVHFHAEMVE3XXY7X2T7BLTLL7XIILZAXNJJH2YONUQC
IR75ZMX32SFFMDNV2I2L22X5JTWCOC4UUBCSPU7S6VHR6HFV6ADQC
BSIUHCGFDFDFGWYMHZB7OVU3Z3IHPEUXRISIOPGZI2RUXZFDS2EQC
ADMKQQGCGVSHHIMVQ4XFRDCG544SBJCYALSKZV45CQQBZ4ACUH2AC
IZEVQF627FA7VV25KJAWYWGGC35LZUUBBQRPN5ZAINTQLPEDRTEAC
5IDB3IWSB6LFW4U772Y7BH5Y3FQOQ7IFWLVXDZE5XS6SKJITFV4QC
PT4276XCOP5NJ3GRFJLIBZKVNVAOATAY5PLWV7FWK6RZW5FTEP5AC
network = "test"
# Signing key for BIP-70 payment requests
signingKeyFile = "/etc/aftok/aftok.bip70.key.pem"
certsFile = "/etc/aftok/aftok.bip70-chain.cert.pem"
exchangeRateServiceURI = "https://blockchain.info/ticker"
bitcoin {
networkMode = "test"
# Signing key for BIP-70 payment requests
signingKeyFile = "/etc/aftok/aftok.bip70.key.pem"
certsFile = "/etc/aftok/aftok.bip70-chain.cert.pem"
exchangeRateServiceURI = "https://blockchain.info/ticker"
bip70Host = "localhost:8443"
}
zcash {
minPayment = 100
}
bip70URL <- bip70PaymentURL (nreq ^. Bitcoin.paymentRequestKey)
mail <- buildBip70PaymentRequestEmail preqCfg req''' bip70URL
let bip70URIGen = Bitcoin.uriGen (pcfg' ^. P.bitcoinBillingOps)
bip70URL <- bip70URIGen (nreq ^. Bitcoin.paymentRequestKey)
mail <- traverse (buildBip70PaymentRequestEmail preqCfg req''') bip70URL
_memoGen ::
DB.MonadDB m =>
Billable Satoshi ->
C.Day ->
C.UTCTime ->
m (Maybe Text)
_memoGen bill billingDate requestTime = do
req <- traverseOf B.project DB.findProjectOrError bill
let template =
(newSTMP . T.unpack)
<$> (bill ^. paymentRequestMemoTemplate)
setAttrs =
setManyAttrib
[ ("project_name", req ^. B.project . projectName),
("subscription", req ^. B.name),
("billing_date", show billingDate),
("issue_time", show requestTime)
]
pure $ fmap (render . setAttrs) template
-- The same URL is used for retrieving a BIP-70 payment request and for submitting
-- the response.
bip70PaymentURL :: Bitcoin.PaymentKey -> AftokM URI
bip70PaymentURL (Bitcoin.PaymentKey k) = do
env <- ask
let hostname = env ^. (dcfg . D.paymentRequestConfig . D.aftokHost)
paymentRequestPath = "https://" <> hostname <> "/pay/" <> k
maybe
( throwError
. ConfigError
$ "Could not parse path "
<> paymentRequestPath
<> " to a valid URI"
)
pure
(parseURI $ show paymentRequestPath)
_payloadGen ::
Monad m =>
Billable Satoshi ->
C.Day ->
C.UTCTime ->
m (Maybe ByteString)
_payloadGen _ _ _ = pure Nothing
readSmtpConfig :: C.Config -> IO SmtpConfig
readSmtpConfig cfg =
SmtpConfig
<$> C.require cfg "smtpHost"
<*> ((fmap . fmap) fromInteger $ C.lookup cfg "smtpPort")
<*> C.require cfg "smtpUser"
<*> C.require cfg "smtpKey"
makeLenses ''BillingConfig
readConnectInfo :: C.Config -> IO ConnectInfo
readConnectInfo cfg =
ConnectInfo
<$> C.require cfg "host"
<*> C.require cfg "port"
<*> C.require cfg "user"
<*> C.require cfg "password"
<*> C.require cfg "database"
readZcashPaymentsConfig :: C.Config -> IO Zcash.PaymentsConfig
readZcashPaymentsConfig cfg =
Zcash.PaymentsConfig
<$> (Zatoshi <$> C.require cfg "minPayment")
toPaymentsConfig :: MonadDB m => BillingConfig -> IO (PaymentsConfig m)
toPaymentsConfig cfg = do
btcCfg <- toBitcoinPaymentsConfig (cfg ^. bitcoinConfig)
let btcOps = Bitcoin.BillingOps _memoGen (_uriGen $ cfg ^. bitcoinConfig . bip70Host) _payloadGen
pure $ PaymentsConfig {
_bitcoinBillingOps = btcOps,
_bitcoinPaymentsConfig = btcCfg,
_zcashPaymentsConfig = cfg ^. zcashConfig
}
_memoGen ::
MonadDB m =>
B.Billable Satoshi ->
Day ->
UTCTime ->
m (Maybe Text)
_memoGen bill billingDate requestTime = do
req <- traverseOf B.project findProjectOrError bill
let template =
(newSTMP . toString)
<$> (bill ^. B.paymentRequestMemoTemplate)
setAttrs =
setManyAttrib
[ ("project_name", req ^. B.project . projectName),
("subscription", req ^. B.name),
("billing_date", show billingDate),
("issue_time", show requestTime)
]
pure $ fmap (render . setAttrs) template
_payloadGen ::
Monad m =>
B.Billable Satoshi ->
Day ->
UTCTime ->
m (Maybe ByteString)
_payloadGen _ _ _ = pure Nothing
-- The same URL is used for retrieving a BIP-70 payment request and for submitting
-- the response.
_uriGen ::
Monad m =>
NS.HostName ->
Bitcoin.PaymentKey
-> m (Maybe URI)
_uriGen hostname (Bitcoin.PaymentKey k) =
let paymentRequestPath = "https://" <> fromString hostname <> "/pay/" <> k
in pure . parseURI $ show paymentRequestPath
import qualified Data.ByteString.Base64 as B64
import Data.ProtocolBuffers (Decode, Encode, decodeMessage, encodeMessage)
import Data.Serialize.Get (runGet)
import Data.Serialize.Put (runPut)
protoBase64 :: Encode a => a -> Text
protoBase64 = B64.encodeBase64 . runPut . encodeMessage
fromBase64Proto :: Decode a => Text -> Either Text a
fromBase64Proto t = (first toText . runGet decodeMessage) <=< B64.decodeBase64 $ encodeUtf8 t
import Aftok.Currency.Bitcoin
import Aftok.Currency.Zcash (_Zatoshi)
import Aftok.Currency.Bitcoin (Address, NetworkMode, Satoshi, _Satoshi, getNetwork)
import Aftok.Currency.Zcash (Zatoshi, _Zatoshi)
Description: (Describe migration here.)
Created: 2021-02-07 02:43:18.803817984 UTC
Depends: 2020-11-25_04-22-24_zcash-support
Apply: |
ALTER TABLE cryptocurrency_accounts ADD COLUMN name text;
ALTER TABLE cryptocurrency_accounts ADD COLUMN description text;
ALTER TABLE cryptocurrency_accounts DROP COLUMN currency;
#!/bin/bash
if [ -f ".env" ]; then
source .env
fi
if [ -z "${AFTOK_HOST}" ]; then
AFTOK_HOST="aftok.com"
fi
if [ -z "${PID}" ]; then
read -p "Project UUID: " PID
echo
fi
if [ -z "${USER}" ]; then
read -p "Username: " USER
echo
fi
read -p "Billable ID: " BID
curl --verbose \
${ALLOW_INSECURE} \
--user $USER \
--header "Content-Type: application/json" \
--data "{}" \
"https://$AFTOK_HOST/api/projects/$PID/billables/$BID/paymentRequests"
readQConfig :: CT.Config -> Maybe PGSConfig -> IO QConfig
readQConfig cfg pc =
QConfig
readServerConfig :: CT.Config -> Maybe PGSConfig -> IO ServerConfig
readServerConfig cfg pc =
ServerConfig
--
-- 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
-- ]
paymentRequestDetailJSON :: (PaymentRequestId, SomePaymentRequestDetail) -> Object
paymentRequestDetailJSON (rid, (SomePaymentRequest req)) =
obj $ ["payment_request_id" .= (rid ^. _PaymentRequestId)] <> fields req
where
fields :: PaymentRequest' (Billable' ProjectId UserId) c -> [Pair]
fields r = case r ^. nativeRequest of
(Zip321Request req') ->
[ "total" .= (r ^. billable . B.amount . to zatsJSON),
"expires_at" .= ((r ^. createdAt) .+^ (r ^. billable . B.requestExpiryPeriod)),
"native_request" .= zip321PaymentRequestJSON req'
]
(Bip70Request req') ->
[ "total" .= (r ^. billable . B.amount . to satsJSON),
"expires_at" .= ((r ^. createdAt) .+^ (r ^. billable . B.requestExpiryPeriod)),
"native_request" .= bip70PaymentRequestJSON req'
]
bip70PaymentRequestJSON :: Bitcoin.PaymentRequest -> Value
bip70PaymentRequestJSON r =
v1 . obj $
[ "bip70_request"
.= object
[ "payment_key" .= (r ^. Bitcoin.paymentRequestKey . Bitcoin._PaymentKey),
"payment_request_protobuf_64" .= (r ^. Bitcoin.bip70Request . to protoBase64)
]
]