diff --git a/.circleci/config.yml b/.circleci/config.yml
index da126d8a83de1c0378b9b5907d178320022b2854..e60115f100dde8f1b2551b964f95e6fd30ebfb9c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -15,7 +15,7 @@
 version: 2.1
 
 jobs:
-  test:
+  build:
     docker:
       # Run in a highly Nix-capable environment.  This lets us use Stack's nix
       # integration and other useful Nix features to specify and run the
@@ -89,8 +89,6 @@ jobs:
             --no-terminal \
             --only-dependencies \
             --fast \
-            --test \
-            --no-run-tests \
             --jobs 1 \
             --interleaved-output"
             nix-shell shell.nix --run "$BUILD"
@@ -111,7 +109,7 @@ jobs:
             - ".stack-work"
 
       - run:
-          name: "Run Tests"
+          name: "Building"
           command: |
             # shell.nix gives us the stack we want.  Then stack.yaml specifies
             # some more of the Nix-based environment to be able to build and
@@ -129,11 +127,6 @@ jobs:
             # times for our dependencies (as well as reduce compiler memory
             # usage which may be important at least for stripe-core).
             #
-            # --test runs the test suite.
-            #
-            # --coverage gathers coverage information during the test run.
-            # Steps below publish the result.
-            #
             # --haddock builds the Haskell API documentation.
             # --haddock-internal builds docs even for unexposed modules.
             # --no-haddock-deps skips building docs for all our dependencies.
@@ -141,8 +134,6 @@ jobs:
               --no-terminal \
               --interleaved-output \
               --fast \
-              --test \
-              --coverage \
               --haddock \
               --haddock-internal \
               --no-haddock-deps"
@@ -161,15 +152,8 @@ jobs:
       - run:
           name: "Prepare Artifacts for Upload"
           command: |
-            mv $(nix-shell shell.nix --run "stack path --local-hpc-root") /tmp
             mv $(nix-shell shell.nix --run "stack path --local-doc-root")/PaymentServer-* /tmp/PaymentServer-docs
 
-      - store_artifacts:
-          # This contains the html coverage report as well as the raw data in
-          # .tix format.
-          path: "/tmp/hpc"
-          destination: "coverage"
-
       - store_artifacts:
           # This contains the html haddock output for the project.
           path: "/tmp/PaymentServer-docs"
@@ -179,4 +163,4 @@ workflows:
   version: 2
   everything:
     jobs:
-      - "test"
+      - "build"
diff --git a/PaymentServer.cabal b/PaymentServer.cabal
index 7fc38d823cedcaf763bf6304360c2f98a0560925..ebbcb7b59acf57c442131a0adab753ba01983769 100644
--- a/PaymentServer.cabal
+++ b/PaymentServer.cabal
@@ -46,47 +46,6 @@ executable PaymentServer-exe
                      , PaymentServer
   default-language:    Haskell2010
 
-test-suite PaymentServer-test
-  type:                exitcode-stdio-1.0
-  hs-source-dirs:      test
-  main-is:             Driver.hs
-  other-modules:       SpecStripe
-                     , SpecPersistence
-                     , SpecRedemption
-                     , Util.WAI
-                     , Util.Gen
-                     , Util.JSON
-                     , Util.Spec
-  build-depends:       base
-                     , PaymentServer
-                     , hspec
-                     , hspec-wai
-                     , hspec-wai-json
-                     , hspec-expectations
-                     , aeson
-                     , stripe-core
-                     , time
-                     , QuickCheck
-                     , quickcheck-instances
-                     , checkers
-                     , wai-extra
-                     , bytestring
-                     , text
-                     , http-types
-                     , tasty
-                     , tasty-th
-                     , tasty-discover
-                     , tasty-quickcheck
-                     , tasty-hspec
-                     , tasty-hunit
-                     , tasty-wai
-                     , servant-server
-                     , containers
-                     , unordered-containers
-                     , ilist
-  ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wmissing-import-lists -Wunused-imports
-  default-language:    Haskell2010
-
 source-repository head
   type:     git
   location: https://github.com/privatestorageio/PaymentServer
diff --git a/README.rst b/README.rst
index 211cf19bfb7a568f1ad99094911e50072c0a8bb2..069ce2c1d60278551c3b4097e8fd949cc3c0d610 100644
--- a/README.rst
+++ b/README.rst
@@ -16,11 +16,7 @@ Build using Stack::
 Testing
 -------
 
-You can run the PaymentServer automated test suite using stack::
-
-  $ stack test
-
-You may also want to perform manual integration testing against Stripe.
+You can perform manual integration testing against Stripe.
 First, run the server::
 
   $ stack run
diff --git a/test/Driver.hs b/test/Driver.hs
deleted file mode 100644
index f2287e7136655bb1b55f1262258bcbd21bb32cb8..0000000000000000000000000000000000000000
--- a/test/Driver.hs
+++ /dev/null
@@ -1,8 +0,0 @@
-{-# OPTIONS_GHC -F -pgmF tasty-discover #-}
-
--- This is a module where we can hang the above preprocessor definition to
--- direct tasty-discover to find our test suite spread across the rest of the
--- modules rooted in this directory.
---
--- See the test-suite definition in PaymentServer.cabal
--- See also https://git.coop/decentral1se/tasty-discover
diff --git a/test/SpecPersistence.hs b/test/SpecPersistence.hs
deleted file mode 100644
index db6d8a5be3f33853454c11bb95f3439b71766128..0000000000000000000000000000000000000000
--- a/test/SpecPersistence.hs
+++ /dev/null
@@ -1,86 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-
---
--- Test suite related to the persistence system.
---
-
-module SpecPersistence where
-
-import Control.Monad.IO.Class
-  ( liftIO
-  )
-import Test.Hspec
-  ( Spec
-  , describe
-  , it
-  )
-import Test.Hspec.Expectations
-  ( shouldReturn
-  )
-import Test.QuickCheck
-  ( Property
-  , property
-  , (==>)
-  )
-import Test.QuickCheck.Monadic
-  ( monadicIO
-  , run
-  , assert
-  , pre
-  )
-import Test.QuickCheck.Instances.Text
-  (
-  )
-import PaymentServer.Persistence
-  ( RedeemError(NotPaid, AlreadyRedeemed)
-  , Voucher
-  , Fingerprint
-  , VoucherDatabase(payForVoucher, redeemVoucher)
-  , memory
-  )
-
--- | A voucher which has not been paid for cannot be redeemed.
-unpaidVoucherNotRedeemable :: VoucherDatabase d => IO d -> Property
-unpaidVoucherNotRedeemable getDB = property $ \voucher fingerprint -> do
-  db <- liftIO getDB
-  redeemVoucher db voucher fingerprint `shouldReturn` Left NotPaid
-
--- | A voucher which is paid for can be redeemed with any fingerprint.
-paidVoucherRedeemable :: VoucherDatabase d => IO d -> Property
-paidVoucherRedeemable getDB = property $ \voucher fingerprint -> do
-  db <- liftIO getDB
-  () <- payForVoucher db voucher
-  redeemVoucher db voucher fingerprint `shouldReturn` Right ()
-
--- | A voucher which is paid for can be redeemed more than once as long as the
--- same fingerprint is used each time.
-paidVoucherMultiRedeemable :: VoucherDatabase d => IO d -> Property
-paidVoucherMultiRedeemable getDB = property $ \voucher fingerprint -> do
-  db <- liftIO getDB
-  () <- payForVoucher db voucher
-  let redeem = redeemVoucher db voucher fingerprint
-  redeem
-  redeem `shouldReturn` Right ()
-
--- | A voucher which is paid for can not be redeemed a second time with a
--- different fingerprint than was used on the first attempt.
-paidVoucherMismatchFingerprint :: VoucherDatabase d => IO d -> Property
-paidVoucherMismatchFingerprint getDB = property $ \voucher fingerprint fingerprint' ->
-  fingerprint /= fingerprint' ==> do
-  db <- liftIO getDB
-  () <- payForVoucher db voucher
-  let redeem = redeemVoucher db voucher
-  redeem fingerprint
-  redeem fingerprint' `shouldReturn` Left AlreadyRedeemed
-
-makeSpec :: VoucherDatabase d => IO d -> Spec
-makeSpec getDB =
-  describe "voucher interactions" $
-  do
-    it "denies redemption of a not-paid-for voucher" $ unpaidVoucherNotRedeemable getDB
-    it "allows redemption of paid-for vouchers" $ paidVoucherRedeemable getDB
-    it "allows multiple redemption as long as the same fingerprint is used" $ paidVoucherMultiRedeemable getDB
-    it "denies a subsequent redemption with a different fingerprint" $ paidVoucherMismatchFingerprint getDB
-
-spec_memory = makeSpec memory
diff --git a/test/SpecRedemption.hs b/test/SpecRedemption.hs
deleted file mode 100644
index 6e85850aef01062a5f0cf0f00075d7c94d817efb..0000000000000000000000000000000000000000
--- a/test/SpecRedemption.hs
+++ /dev/null
@@ -1,236 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-
--- | Test suite related to voucher redemption.
-
-module SpecRedemption where
-
-import Data.ByteString
-  ( ByteString
-  )
-import Text.Printf
-  ( printf
-  )
-import Data.Aeson
- ( decode
- , encode
- )
-import Servant
-  ( Application
-  , Proxy(Proxy)
-  , serve
-  )
-import Test.Tasty.Providers
-  ( TestName
-  , singleTest
-  )
-import Test.Tasty
-  ( TestTree
-  , testGroup
-  , withResource
-  )
-import Test.Tasty.HUnit
-  ( Assertion
-  , testCase
-  )
-import Test.Tasty.Wai
-  ( testWai
-  , assertStatus'
-  , assertBody
-  , assertHeader
-  , get
-  )
-
-import Test.Tasty.QuickCheck
-  ( testProperty
-  )
-
-import Network.HTTP.Types
-  ( status200
-  , status405
-  )
-
-import Test.Hspec
-  ( Spec
-  , parallel
-  , describe
-  , it
-  , runIO
-  )
-import Network.HTTP.Types
-  ( Header
-  )
-import Test.Hspec.Wai
-  ( ResponseMatcher(matchBody, matchHeaders)
-  , (<:>)
-  , WaiExpectation
-  , Body
-  , MatchBody(MatchBody)
-  , with
-  , shouldRespondWith
-  , liftIO
-  )
-import Test.QuickCheck
-  ( ioProperty
-  )
-import Test.Hspec.Wai.QuickCheck
-  ( Testable(toProperty)
-  , WaiProperty(unWaiProperty)
-  , property
-  )
-import Test.QuickCheck.Instances.Text ()
-import Util.Spec
-  ( wrongMethodNotAllowed
-  , nonJSONUnsupportedMediaType
-  , wrongJSONInvalidRequest
-  )
-import Util.WAI
-  ( postJSON
-  )
-import PaymentServer.Issuer
-  ( BlindedToken
-  , ChallengeBypass(ChallengeBypass)
-  , Issuer
-  , trivialIssue
-  )
-import PaymentServer.Redemption
-  ( RedemptionAPI
-  , Redeem(Redeem)
-  , Result(Failed, Succeeded)
-  , redemptionServer
-  )
-import PaymentServer.Persistence
-  ( RedeemError(NotPaid)
-  , Voucher
-  , VoucherDatabase(payForVoucher, redeemVoucher)
-  , memory
-  )
-
-redemptionAPI :: Proxy RedemptionAPI
-redemptionAPI = Proxy
-
-app :: VoucherDatabase d => Issuer -> d -> Application
-app issue = serve redemptionAPI . redemptionServer issue
-
-path = "/"
-
-propertyRedeem :: ByteString -> Voucher -> [BlindedToken] -> ResponseMatcher -> WaiExpectation
-propertyRedeem path voucher tokens matcher =
-  postJSON path (encode $ Redeem voucher tokens) `shouldRespondWith` matcher
-
--- | A VoucherDatabaseTestDouble has a VoucherDatabase instance which provides
--- a number of different behaviors which are useful to be able to directly
--- test against.
-data VoucherDatabaseTestDouble
-  -- | A RefuseRedemption database always refuses redemption with a given error.
-  = RefuseRedemption RedeemError
-  -- | A PermitRedemption database always permits redemption.
-  | PermitRedemption
-  deriving (Show)
-
-instance VoucherDatabase VoucherDatabaseTestDouble where
-  payForVoucher _ voucher = return ()
-  redeemVoucher (RefuseRedemption err) _ _ = return $ Left err
-  redeemVoucher PermitRedemption _ _ = return $ Right ()
-
-spec_redemption :: Spec
-spec_redemption = parallel $ do
-  database <- runIO memory
-  with (return $ app trivialIssue database) $
-    do
-      describe (printf "error behavior of POST %s" (show path)) $
-        do
-          wrongMethodNotAllowed "GET" path
-          nonJSONUnsupportedMediaType path
-          wrongJSONInvalidRequest path "{}"
-
-      -- I would rather write these two as property tests but I don't know
-      -- how.
-      describe "double redemption" $ do
-        it "succeeds with the same tokens" $ do
-          let voucher = "abc" :: Voucher
-          let tokens = [ "def", "ghi" ] :: [BlindedToken]
-          liftIO $ payForVoucher database voucher
-          propertyRedeem path voucher tokens 200
-          propertyRedeem path voucher tokens 200
-
-        it "fails with different tokens" $ do
-          let voucher = "jkl" :: Voucher
-          let firstTokens = [ "mno", "pqr" ] :: [BlindedToken]
-          let secondTokens = [ "stu", "vwx" ] :: [BlindedToken]
-          liftIO $ payForVoucher database voucher
-          propertyRedeem path voucher firstTokens 200
-          propertyRedeem path voucher secondTokens 400
-
-
-  -- describe "redemption" $ do
-  --   with (return $ app trivialIssue (RefuseRedemption NotPaid)) $
-  --     it "receives a failure response when the voucher is not paid" $ property $
-  --       \(voucher :: Voucher) (tokens :: [BlindedToken]) ->
-  --         propertyRedeem path voucher tokens 400
-  --         { matchBody = matchJSONBody Failed
-  --         -- major/minor, fine.  charset=utf-8... okay.  but really this is
-  --         -- overspecified by encoding the exact byte sequence.  I'd rather
-  --         -- assert semantic equality.
-  --         , matchHeaders = ["Content-Type" <:> "application/json;charset=utf-8"]
-  --         }
-
-  --   with (return $ app trivialIssue PermitRedemption) $
-  --     it "receive a success response when redemption succeeds" $ property
-  --     \(voucher :: Voucher) (tokens :: [BlindedToken]) -> do
-  --       (ChallengeBypass key signatures proof) <- trivialIssue tokens
-  --       return $
-  --         propertyRedeem path voucher tokens 200
-  --           { matchBody = matchJSONBody $ Succeeded key signatures proof
-  --           , matchHeaders = ["Content-Type" <:> "application/json;charset=utf-8"]
-  --           }
-
-    -- it "receive 200 (OK) when the voucher is paid and previously redeemed with the same tokens" $
-    --   property $ \(voucher :: Voucher) (tokens :: [BlindedToken]) ->
-    --   do
-    --     liftIO $ payForVoucher database voucher
-    --     postJSON path (encode $ Redeem voucher tokens) `shouldRespondWith` 200
-    --     postJSON path (encode $ Redeem voucher tokens) `shouldRespondWith` 200
-
-    -- it "receive 400 (OK) when the voucher is paid and previously redeemed with different tokens" $
-    --   property $ \(voucher :: Voucher) (firstTokens :: [BlindedToken]) (secondTokens :: [BlindedToken]) ->
-    --   do
-    --     liftIO $ payForVoucher database voucher
-    --     postJSON path (encode $ Redeem voucher firstTokens) `shouldRespondWith` 200
-    --     postJSON path (encode $ Redeem voucher secondTokens) `shouldRespondWith` 400
-
-matchJSONBody :: Result -> MatchBody
-matchJSONBody expected =
-  let
-    bodyMatcher :: [Header] -> Body -> Maybe String
-    bodyMatcher headers actualBody =
-      case decode actualBody of
-        Nothing ->
-          Just $ "failed to decode body as value of expected type: " ++ show actualBody
-        Just actual ->
-          if actual == expected then
-            Nothing
-          else
-            Just $ "decoded body does not equal expected value: " ++ show actual ++ show expected
-  in
-    MatchBody bodyMatcher
-
--- testWithDatabase :: VoucherDatabase d => d -> Assertion
--- testWithDatabase database =
---   let
---     testApp = app trivialIssue database
---   in
-
--- test_redemption :: TestTree
--- test_redemption =
---   let
---     testApp = memory >>= (return . app trivialIssue)
---   in
---   withResource testApp (\x -> return ()) $ \getApp ->
---   testGroup "Voucher Redemption"
---   [ testWai' getApp "a non-POST receives a 405 (Method Not Allowed) response" $
---     do
---       res <- get "/"
---       assertStatus' status405 res
---       assertBody "blub" res
---   ]
diff --git a/test/SpecStripe.hs b/test/SpecStripe.hs
deleted file mode 100644
index d2a281b1f5eadef91c4fee31e2f73b387f11e17a..0000000000000000000000000000000000000000
--- a/test/SpecStripe.hs
+++ /dev/null
@@ -1,126 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ScopedTypeVariables #-}
-
---
--- Test suite for Stripe support in the payment server.
---
-
-module SpecStripe where
-
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as LazyBS
-import Data.Aeson
-  ( encode
-  )
-import Test.Hspec
-  ( Spec
-  , parallel
-  , describe
-  , it
-  )
-import Test.Hspec.Wai
-  ( WaiSession
-  , WaiExpectation
-  , MatchBody(MatchBody)
-  , ResponseMatcher(matchBody)
-  , Body
-  , with
-  , post
-  , shouldRespondWith
-  , liftIO
-  )
-import Test.Hspec.Wai.QuickCheck
-  ( property
-  )
-import Test.QuickCheck
-  ( Property
-  , Gen
-  , arbitrary
-  , generate
-  , forAll
-  , (===)
-  , (=/=)
-  )
-import Util.WAI
-  ( postJSON
-  )
-import Util.Gen
-  ( ChargeEvents(GoodChargeEvent, BadChargeEvent)
-  , chargeSucceededEvents
-  , metaDatasWithVoucher
-  , metaDatasWithoutVoucher
-  )
-import Util.JSON
-  ( -- ToJSON instance for Event
-  )
-import Network.HTTP.Types
-  ( Header
-  )
-import Servant
-  ( Application
-  , Proxy(Proxy)
-  , serve
-  )
-import Web.Stripe.Types
-  ( MetaData(MetaData)
-  )
-import PaymentServer.Processors.Stripe
-  ( StripeAPI
-  , stripeServer
-  , getVoucher
-  )
-import PaymentServer.Persistence
-  ( Voucher
-  , memory
-  )
-import Util.Spec
-  ( wrongMethodNotAllowed
-  , nonJSONUnsupportedMediaType
-  , wrongJSONInvalidRequest
-  )
-
-stripeAPI :: Proxy StripeAPI
-stripeAPI = Proxy
-
-app :: IO Application
-app = serve stripeAPI . stripeServer <$> memory
-
-spec_webhook :: Spec
-spec_webhook = with app $ parallel $ do
-  describe "error behavior of POST /webhook" $ do
-    wrongMethodNotAllowed "GET" "/webhook"
-    nonJSONUnsupportedMediaType "/webhook"
-    wrongJSONInvalidRequest "/webhook" "{}"
-
-  -- I would like to make most or all of these into property tests.  *This*
-  -- test shows how you can do it.  Yay.  The main thing (for me, anyway) to
-  -- remember is to use `property` from Test.Hspec.Wai.QuickCheck and not from
-  -- `Test.QuickCheck`. :/ Unsure whether I love the apparent Haskell
-  -- convention of giving the same name to *similar* functions.
-  describe "success behavior of POST /webhook" $
-    it "responds to a JSON Event body with 200 (OK)" $
-    let
-      test e =
-        postJSON "/webhook" (encode e) `shouldRespondWith` 200 { matchBody = MatchBody bodyMatcher }
-      -- For now these are the same.  Maybe they always will be?  The HTTP
-      -- behavior is the same though the backend behavior may differ.  Note
-      -- that a "test_" prefix would cause tasty-discover to find this and try
-      -- to call it - but it can't since it's not a top-level, let alone
-      -- exported.
-      xtest_postWithEventBody (GoodChargeEvent e) = test e
-      xtest_postWithEventBody (BadChargeEvent e) = test e
-    in
-      property xtest_postWithEventBody
-
-
-bodyMatcher :: [Network.HTTP.Types.Header] -> Body -> Maybe String
-bodyMatcher _ "{}" = Nothing
-bodyMatcher _ body = Just $ show body
-
-prop_getVoucherFindsVoucher :: Property
-prop_getVoucherFindsVoucher = forAll metaDatasWithVoucher $ \x ->
-  getVoucher x =/= Nothing
-
-prop_getVoucherWithoutVoucher :: Property
-prop_getVoucherWithoutVoucher = forAll metaDatasWithoutVoucher $ \x ->
-  getVoucher x === Nothing
diff --git a/test/Util/Gen.hs b/test/Util/Gen.hs
deleted file mode 100644
index ffba41237a591d05a7834df9663564f46d7b5d2b..0000000000000000000000000000000000000000
--- a/test/Util/Gen.hs
+++ /dev/null
@@ -1,187 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Util.Gen
-  ( chargeSucceededEvents
-  , metaDatasWithoutVoucher
-  , metaDatasWithVoucher
-  , posixTimes
-  , hasVoucher
-  , ChargeEvents(GoodChargeEvent, BadChargeEvent)
-  ) where
-
-import Data.Text
-  ( Text
-  )
-import Data.List.Index
-  ( insertAt
-  )
-import Data.Time.Clock
-  ( UTCTime(UTCTime)
-  )
-import Data.Time.Clock.POSIX
-  ( POSIXTime
-  , posixSecondsToUTCTime
-  )
-import Web.Stripe.Types
-  ( Charge(Charge)
-  , StatementDescription(StatementDescription)
-  , MetaData(MetaData)
-  , Description(Description)
-  , CustomerId(CustomerId)
-  , InvoiceId(InvoiceId)
-  , ChargeId(ChargeId)
-  , Expandable(Id)
-  , Currency(USD, UnknownCurrency)
-  , Amount(Amount)
-  , StripeList(StripeList, list, totalCount, hasMore)
-  )
-import Web.Stripe.Event
-  ( Event(Event, eventId, eventCreated, eventLiveMode, eventType, eventData, eventObject, eventPendingWebHooks, eventRequest)
-  , EventType(ChargeSucceededEvent)
-  , EventData(ChargeEvent)
-  , EventId(EventId)
-  )
-import Test.QuickCheck
-  ( Gen
-  , Arbitrary
-  , arbitrary
-  , Positive(Positive)
-  , oneof
-  , suchThat
-  , suchThatMap
-  )
-import Test.QuickCheck.Instances.Tuple
-  ( (>*<)
-  )
-import Test.QuickCheck.Instances.Time
-  ( -- Get the `Gen UTCTime` instance
-  )
-import Test.QuickCheck.Instances.Text
-  ( -- Get the `Gen Text` instance
-  )
-import PaymentServer.Persistence
-  ( Voucher
-  )
-
-instance Arbitrary Charge where
-  arbitrary = charges True
-
-instance Semigroup (StripeList a) where
-  -- Very weak implementation
-  x <> y = StripeList (list x <> list y) "" "" Nothing (hasMore x || hasMore y)
-
-instance Monoid (StripeList a) where
-  mempty = StripeList [] "" "" (Just 0) False
-
-instance Arbitrary Amount where
-  arbitrary = Amount <$> arbitrary
-
-
-instance Arbitrary EventId where
-  arbitrary = EventId <$> arbitrary
-
-
-instance Arbitrary StatementDescription where
-  arbitrary = StatementDescription <$> arbitrary
-
-metaDatasWithoutVoucher = (arbitrary :: Gen MetaData) `suchThat` (not . hasVoucher)
--- Just filtering out random metadatas that don't have a voucher makes for an
--- incredibly inefficient generator.  So start without a voucher and then add
--- one.
-metaDatasWithVoucher = ((arbitrary :: Gen Voucher) >*< metaDatasWithoutVoucher) `suchThatMap` (Just. uncurry addVoucher)
-
-addVoucher :: Voucher -> MetaData -> MetaData
-addVoucher voucher (MetaData []) = MetaData [("Voucher", voucher)]
-addVoucher voucher (MetaData items) =
-  MetaData (insertAt (1234567 `mod` length items) ("Voucher", voucher) items)
-
-instance Arbitrary MetaData where
-  arbitrary = MetaData <$> arbitrary
-
-instance Arbitrary Description where
-  arbitrary = Description <$> arbitrary
-
-
-instance Arbitrary InvoiceId where
-  arbitrary = InvoiceId <$> arbitrary
-
-
-instance Arbitrary ChargeId where
-  arbitrary = ChargeId <$> arbitrary
-
-
-instance Arbitrary CustomerId where
-  arbitrary = CustomerId <$> arbitrary
-
-
-instance Arbitrary a => Arbitrary (Expandable a) where
-  arbitrary = Id <$> arbitrary
-
-chargeSucceededEvents :: Bool -> Gen Event
-chargeSucceededEvents withVoucher =
-  Event
-  <$> arbitrary -- eventId
-  <*> posixTimes -- eventCreated
-  <*> arbitrary -- eventLiveMode
-  <*> return ChargeSucceededEvent -- eventType
-  <*> chargeEvents withVoucher -- eventData
-  <*> return "event" -- eventObject
-  <*> arbitrary -- eventPendingWebHooks
-  <*> arbitrary -- eventRequest
-
-
-chargeEvents :: Bool -> Gen EventData
-chargeEvents withVoucher =
-  ChargeEvent <$> charges withVoucher
-
-charges :: Bool -> Gen Charge
-charges withVoucher =
-  Charge
-  <$> arbitrary         --   chargeId :: ChargeId
-  <*> return "charge"   --   chargeObject :: Text
-  <*> posixTimes        --   chargeCreated :: UTCTime
-  <*> arbitrary         --   chargeLiveMode :: Bool
-  <*> arbitrary         --   chargePaid :: Bool
-  <*> arbitrary         --   chargeAmount :: Amount
-  <*> oneof
-  [ return UnknownCurrency
-  , return USD
-  ]                     --   chargeCurrency :: Currency
-  <*> return False      --   chargeRefunded :: Bool
-  <*> return Nothing    --   chargeCreditCard :: Maybe Card
-  <*> arbitrary         --   chargeCaptured :: Bool
-  <*> return mempty     --   chargeRefunds :: StripeList Refund
-  <*> return Nothing    --   chargeBalanceTransaction :: Maybe (Expandable TransactionId)
-  <*> return Nothing    --   chargeFailureMessage :: Maybe Text
-  <*> return Nothing    --   chargeFailureCode :: Maybe Text
-  <*> return 0          --   chargeAmountRefunded :: Int
-  <*> arbitrary         --   chargeCustomerId :: Maybe (Expandable CustomerId)
-  <*> return Nothing    --   chargeInvoice :: Maybe (Expandable InvoiceId)
-  <*> arbitrary         --   chargeDescription :: Maybe Description
-  <*> return Nothing    --   chargeDispute :: Maybe Dispute
-  <*> (
-  if withVoucher then
-    metaDatasWithVoucher
-  else
-    metaDatasWithoutVoucher
-  )                     --   chargeMetaData :: MetaData
-  <*> arbitrary         --   chargeStatementDescription :: Maybe StatementDescription
-  <*> arbitrary         --   chargeReceiptEmail :: Maybe Text
-  <*> arbitrary         --   chargeReceiptNumber :: Maybe Text
-
-data ChargeEvents
-  = GoodChargeEvent Event
-  | BadChargeEvent Event
-  deriving (Show, Eq)
-
-instance Arbitrary ChargeEvents where
-  arbitrary = oneof
-    [ chargeSucceededEvents True `suchThatMap` (Just . GoodChargeEvent)
-    , chargeSucceededEvents False `suchThatMap` (Just . BadChargeEvent)
-    ]
-
-posixTimes :: Gen UTCTime
-posixTimes = (arbitrary :: Gen Integer) `suchThatMap` (Just . posixSecondsToUTCTime . fromIntegral . abs)
-
-hasVoucher :: MetaData -> Bool
-hasVoucher (MetaData items) = elem "Voucher" . map fst $ items
diff --git a/test/Util/JSON.hs b/test/Util/JSON.hs
deleted file mode 100644
index f43be9f362f703a9cc376cfa3fb8f72dd6dce601..0000000000000000000000000000000000000000
--- a/test/Util/JSON.hs
+++ /dev/null
@@ -1,175 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Util.JSON where
-
-import Data.Time.Clock.POSIX
-  ( utcTimeToPOSIXSeconds
-  )
-import Data.HashMap.Lazy
-  ( fromList
-  )
-import qualified Data.Sequence as Seq
-import Web.Stripe.Types
-  ( Charge(Charge)
-  , ChargeId(ChargeId)
-  , InvoiceId(InvoiceId)
-  , Amount(Amount)
-  , Currency(USD, UnknownCurrency)
-  , MetaData(MetaData)
-  , StripeList(StripeList)
-  , Refund(Refund)
-  , RefundId(RefundId)
-  , TransactionId(TransactionId)
-  , Expandable(Id)
-  )
-import Web.Stripe.Event
-  ( Event(Event)
-  , EventData(ChargeEvent)
-  , EventType(ChargeSucceededEvent)
-  , EventId(EventId)
-  )
-import Data.Aeson
-  ( ToJSON(toJSON)
-  , Value(String, Number, Object, Array)
-  , object
-  , (.=)
-  )
-import Data.Aeson.Types
-  ( listValue
-  )
-
-instance ToJSON Event where
-  toJSON (Event
-           eventId
-           eventCreated
-           eventLiveMode
-           eventType
-           eventData
-           eventObject
-           eventPendingWebHooks
-           eventRequest)
-    = object
-    [ "id" .= eventId
-    , "object" .= eventObject
-    , "api_version" .= String "2018-05-21"
-    , "created" .= utcTimeToPOSIXSeconds eventCreated
-    , "data" .= object [ "object" .= eventData ]
-    , "type" .= eventType
-    , "livemode" .= eventLiveMode
-    , "pending_webhooks" .= eventPendingWebHooks
-    , "request" .= eventRequest
-    ]
-
-instance ToJSON a => ToJSON (Expandable a) where
-  toJSON (Id eId) = toJSON eId
-
-instance ToJSON EventId where
-  toJSON (EventId eId) = String eId
-
-instance ToJSON InvoiceId where
-  toJSON (InvoiceId iId) = String iId
-
-instance ToJSON ChargeId where
-  toJSON (ChargeId cId) = String cId
-
-instance ToJSON RefundId where
-  toJSON (RefundId rId) = String rId
-
-instance ToJSON TransactionId where
-  toJSON (TransactionId tId) = String tId
-
-instance ToJSON Currency where
-  toJSON USD = "USD"
-  toJSON UnknownCurrency = "???"
-
-instance ToJSON EventType where
-  toJSON ChargeSucceededEvent = "charge.succeeded"
-
-instance ToJSON EventData where
-  toJSON (ChargeEvent charge) = toJSON charge
-
-instance ToJSON Amount where
-  toJSON (Amount a) = Number $ fromIntegral a
-
-instance ToJSON MetaData where
-  toJSON (MetaData items) = (Object . fromList . map (\(k, v) -> (k, String v))) items
-
-instance ToJSON Refund where
-  toJSON (Refund
-           refundId
-           refundAmount
-           refundCurrency
-           refundCreated
-           refundObject
-           refundCharge
-           refundBalanceTransaction
-           refundMetaData
-         )
-    = object
-    [ "id" .= refundId
-    , "amount" .= refundAmount
-    , "currency" .= refundCurrency
-    , "created" .= utcTimeToPOSIXSeconds refundCreated
-    , "object" .= refundObject
-    , "charge" .= refundCharge
-    , "balance_transaction" .= refundBalanceTransaction
-    , "metadata" .= refundMetaData
-    ]
-
-instance (ToJSON a) => ToJSON (StripeList a) where
-  toJSON (StripeList elements stripeUrl obj totalCount hasMore)
-    = object
-    [ "data" .= toJSON elements
-    , "url" .= stripeUrl
-    , "object" .= obj
-    , "has_more" .= hasMore
-    ]
-
-instance ToJSON Charge where
-  toJSON (Charge
-           chargeId
-           chargeObject
-           chargeCreated
-           chargeLiveMode
-           chargePaid
-           chargeAmount
-           chargeCurrency
-           chargeRefunded
-           chargeCreditChard
-           chargeCaptured
-           chargeRefunds
-           chargeBalanceTransaction
-           chargeFailureMessage
-           chargeFailureCode
-           chargeAmountRefunded
-           chargeCustomerId
-           chargeInvoice
-           chargeDescription
-           chargeDispute
-           chargeMetaData
-           chargeStatementDescription
-           chargeReceiptEmail
-           chargeNumber
-         )
-    = object
-    [ "id"         .= chargeId
-    , "object"     .= chargeObject
-    , "amount"     .= chargeAmount
-    , "amount_refunded" .= chargeAmountRefunded
-    -- , "balance_transaction" .= chargeBalanceTransaction
-    , "captured" .= chargeCaptured
-    , "created"    .= utcTimeToPOSIXSeconds chargeCreated
-    , "currency"   .= chargeCurrency
-    -- , "customer" .= chargeCustomerId
-    -- , "description" .= chargeDescription
-    -- , "dispute" .= chargeDispute
-    -- , "failure_code" .= chargeFailureCode
-    -- , "failure_message" .= chargeFailureMessage
-    -- , "invoice" .= chargeInvoice
-    , "livemode" .= chargeLiveMode
-    , "metadata" .= chargeMetaData
-    , "paid" .= chargePaid
-    , "receipt_email" .= chargeReceiptEmail
-    , "refunded" .= chargeRefunded
-    , "refunds" .= chargeRefunds
-    ]
diff --git a/test/Util/Spec.hs b/test/Util/Spec.hs
deleted file mode 100644
index 9ce6f8de4f85163cc5e050c03511d95aac801083..0000000000000000000000000000000000000000
--- a/test/Util/Spec.hs
+++ /dev/null
@@ -1,32 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Util.Spec
-  ( wrongMethodNotAllowed
-  , nonJSONUnsupportedMediaType
-  , wrongJSONInvalidRequest
-  ) where
-
-import Test.Hspec
-  ( it
-  )
-import Test.Hspec.Wai
-  ( post
-  , request
-  , shouldRespondWith
-  )
-
-import Util.WAI
-  ( postJSON
-  )
-
-wrongMethodNotAllowed method path =
-  it "responds to an unsupported method with 405 (Method Not Allowed)" $
-  request method path [] "" `shouldRespondWith` 405
-
-nonJSONUnsupportedMediaType path =
-  it "responds to non-JSON Content-Type with 415 (Unsupported Media Type)" $
-  post path "xxx" `shouldRespondWith` 415
-
-wrongJSONInvalidRequest path json =
-  it "responds to JSON body representing the wrong data with 400 (Invalid Request)" $
-  postJSON path json `shouldRespondWith` 400
diff --git a/test/Util/WAI.hs b/test/Util/WAI.hs
deleted file mode 100644
index 53d29ff74af1c937a477b89390868a23d7090d3d..0000000000000000000000000000000000000000
--- a/test/Util/WAI.hs
+++ /dev/null
@@ -1,24 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-
-module Util.WAI
-  ( postJSON
-  ) where
-
-import Data.ByteString.Lazy as LazyBS
-import Data.ByteString as BS
-import Test.Hspec.Wai
-  ( WaiSession
-  , request
-  )
-import Network.HTTP.Types.Method
-  ( methodPost
-  )
-import Network.Wai.Test
-  ( SResponse
-  )
-
--- Post some JSON to a path.
--- Return a function from path to a response
-postJSON :: BS.ByteString -> (LazyBS.ByteString -> WaiSession SResponse)
-postJSON path =
-  request methodPost path [("Content-Type", "application/json")]