diff --git a/PaymentServer.cabal b/PaymentServer.cabal index 165887ef0111186ff8eda3db078cdda6c634fc6d..2746523024ff2852dc5308d95cf2c1b690f4e45d 100644 --- a/PaymentServer.cabal +++ b/PaymentServer.cabal @@ -35,6 +35,7 @@ library , text , containers , cryptonite + , sqlite-simple default-language: Haskell2010 ghc-options: -Wmissing-import-lists -Wunused-imports pkgconfig-depends: ristretto diff --git a/src/PaymentServer/Persistence.hs b/src/PaymentServer/Persistence.hs index d08f12a67a393c8f4dea16b02a7a3ece162eb4f4..d13414d6221da189d2fdbb58833c8be5dc868cde 100644 --- a/src/PaymentServer/Persistence.hs +++ b/src/PaymentServer/Persistence.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeSynonymInstances #-} module PaymentServer.Persistence ( Voucher , Fingerprint @@ -21,6 +23,10 @@ import Data.IORef , modifyIORef , readIORef ) +import qualified Database.SQLite.Simple as Sqlite +import Database.SQLite.Simple.FromRow + ( FromRow(fromRow) + ) -- | A voucher is a unique identifier which can be associated with a payment. -- A paid voucher can be redeemed for ZKAPs which can themselves be exchanged @@ -103,3 +109,34 @@ memory = do paid <- newIORef mempty redeemed <- newIORef mempty return $ Memory paid redeemed + +instance VoucherDatabase Sqlite.Connection where + -- payForVoucher :: Sqlite.Connection -> Voucher -> IO () + payForVoucher dbConn voucher = do + undefined + -- redeemVoucher :: Sqlite.Connection -> Voucher -> Fingerprint -> IO (Either RedeemError ()) + redeemVoucher dbConn voucher fingerprint = do + unpaid <- Set.notMember voucher <$> getPaidVouchers dbConn + existingFingerprint <- getVoucherFingerprint dbConn voucher + case (unpaid, existingFingerprint) of + (True, _) -> + return $ Left NotPaid + (False, []) -> + -- TODO: insert voucher and fingerprint into the redeemed table + return $ Right () + (False, [fingerprint']) -> + if fingerprint == fingerprint' then + return $ Right () + else + return $ Left AlreadyRedeemed + +instance FromRow Fingerprint where + fromRow = Sqlite.field + +getPaidVouchers :: Sqlite.Connection -> IO (Set.Set Voucher) +getPaidVouchers dbConn = Set.fromList <$> + Sqlite.query_ dbConn "SELECT DISTINCT name FROM vouchers" + +getVoucherFingerprint :: Sqlite.Connection -> Voucher -> IO [Fingerprint] +getVoucherFingerprint dbConn voucher = do + Sqlite.query dbConn "SELECT redeemed.fingerprint FROM vouchers INNER JOIN redeemed ON vouchers.id = redeemed.voucher_id AND vouchers.name = ?" (Sqlite.Only voucher)