From 7e81985ee21a208cd79b35f81bed9f87e08c75a9 Mon Sep 17 00:00:00 2001
From: Jean-Paul Calderone <exarkun@twistedmatrix.com>
Date: Tue, 26 Nov 2019 14:22:39 -0500
Subject: [PATCH] Command line options to configure Stripe API endpoint

---
 src/PaymentServer/Main.hs              | 60 ++++++++++++++++++++++----
 src/PaymentServer/Processors/Stripe.hs | 14 +++---
 src/PaymentServer/Server.hs            | 18 +++++---
 3 files changed, 67 insertions(+), 25 deletions(-)

diff --git a/src/PaymentServer/Main.hs b/src/PaymentServer/Main.hs
index 07c3521..65afc14 100644
--- a/src/PaymentServer/Main.hs
+++ b/src/PaymentServer/Main.hs
@@ -18,6 +18,9 @@ import Data.Maybe
 import Data.Text
   ( Text
   )
+import Data.ByteString
+  ( ByteString
+  )
 import Data.Default
   ( def
   )
@@ -50,6 +53,14 @@ import Network.Wai.Middleware.RequestLogger
   , outputFormat
   , mkRequestLogger
   )
+
+import Web.Stripe.Client
+  ( Protocol(HTTPS)
+  , StripeConfig(StripeConfig)
+  , StripeKey(StripeKey)
+  )
+import qualified Web.Stripe.Client as Stripe
+
 import PaymentServer.Persistence
   ( memory
   , getDBConnection
@@ -103,13 +114,16 @@ data Database =
   deriving (Show, Eq, Ord, Read)
 
 data ServerConfig = ServerConfig
-  { issuer          :: Issuer
-  , signingKeyPath  :: Maybe FilePath
-  , database        :: Database
-  , databasePath    :: Maybe Text
-  , endpoint        :: Endpoint
-  , stripeKeyPath   :: FilePath
-  , corsOrigins     :: [Origin]
+  { issuer                 :: Issuer
+  , signingKeyPath         :: Maybe FilePath
+  , database               :: Database
+  , databasePath           :: Maybe Text
+  , endpoint               :: Endpoint
+  , stripeKeyPath          :: FilePath
+  , stripeEndpointUrl      :: ByteString
+  , stripeEndpointProtocol :: Protocol
+  , stripeEndpointPort     :: Int
+  , corsOrigins            :: [Origin]
   }
   deriving (Show, Eq)
 
@@ -182,6 +196,21 @@ sample = ServerConfig
   <*> option str
   ( long "stripe-key-path"
     <> help "Path to Stripe Secret key" )
+  <*> option str
+  ( long "stripe-endpoint-url"
+    <> help "The root endpoint of the Stripe HTTP API"
+    <> value "api.stripe.com"
+    <> showDefault )
+  <*> option auto
+  ( long "stripe-endpoint-protocol"
+    <> help "The Stripe HTTP API protocol (HTTP or HTTPS)."
+    <> value HTTPS
+    <> showDefault )
+  <*> option auto
+  ( long "stripe-endpoint-port"
+    <> help "The Stripe HTTP API endpoint port number."
+    <> value 443
+    <> showDefault )
   <*> many ( option str
              ( long "cors-origin"
              <> help "An allowed `Origin` for the purposes of CORS (zero or more)." ) )
@@ -252,6 +281,19 @@ getApp config =
         (Memory, Nothing) -> Right memory
         (SQLite3, Just path) -> Right (getDBConnection path)
         _ -> Left "invalid options"
+
+    stripeConfig ServerConfig
+      { stripeKeyPath
+      , stripeEndpointUrl
+      , stripeEndpointProtocol
+      , stripeEndpointPort
+      } =
+      do
+        key <- B.readFile stripeKeyPath
+        return $
+          StripeConfig
+          (StripeKey key)
+          (Just $ Stripe.Endpoint stripeEndpointUrl stripeEndpointProtocol stripeEndpointPort)
   in do
     issuer <- getIssuer config
     case issuer of
@@ -265,9 +307,9 @@ getApp config =
             exitFailure
           Right getDB -> do
             db <- getDB
-            key <- B.readFile (stripeKeyPath config)
+            stripeConfig' <- stripeConfig config
             let
               origins = corsOrigins config
-              app = paymentServerApp origins key issuer db
+              app = paymentServerApp origins stripeConfig' issuer db
             logger <- mkRequestLogger (def { outputFormat = Detailed True})
             return $ logger app
diff --git a/src/PaymentServer/Processors/Stripe.hs b/src/PaymentServer/Processors/Stripe.hs
index 25a63fe..2c80548 100644
--- a/src/PaymentServer/Processors/Stripe.hs
+++ b/src/PaymentServer/Processors/Stripe.hs
@@ -6,7 +6,6 @@ module PaymentServer.Processors.Stripe
   ( StripeAPI
   , stripeServer
   , getVoucher
-  , StripeSecretKey
   ) where
 
 import Control.Monad.IO.Class
@@ -87,8 +86,6 @@ import PaymentServer.Persistence
   , PaymentError(AlreadyPaid, PaymentFailed)
   )
 
-type StripeSecretKey = ByteString
-
 data Acknowledgement = Ok
 
 instance ToJSON Acknowledgement where
@@ -105,8 +102,8 @@ getVoucher (MetaData []) = Nothing
 getVoucher (MetaData (("Voucher", value):xs)) = Just value
 getVoucher (MetaData (x:xs)) = getVoucher (MetaData xs)
 
-stripeServer :: VoucherDatabase d => StripeSecretKey -> d -> Server StripeAPI
-stripeServer key d = charge d key
+stripeServer :: VoucherDatabase d => StripeConfig -> d -> Server StripeAPI
+stripeServer stripeConfig d = charge d stripeConfig
 
 -- | Browser facing API that takes token, voucher and a few other information
 -- and calls stripe charges API. If payment succeeds, then the voucher is stored
@@ -132,8 +129,8 @@ instance FromJSON Charges where
 
 -- | call the stripe Charge API (with token, voucher in metadata, amount, currency etc
 -- and if the Charge is okay, then set the voucher as "paid" in the database.
-charge :: VoucherDatabase d => d -> StripeSecretKey -> Charges -> Handler Acknowledgement
-charge d key (Charges token voucher amount currency) = do
+charge :: VoucherDatabase d => d -> StripeConfig -> Charges -> Handler Acknowledgement
+charge d stripeConfig (Charges token voucher amount currency) = do
   currency' <- getCurrency currency
   result <- liftIO (try (payForVoucher d voucher (completeStripeCharge currency')))
   case result of
@@ -150,10 +147,9 @@ charge d key (Charges token voucher amount currency) = do
           Just currency' -> return currency'
           Nothing -> throwError unsupportedCurrency
 
-      config = StripeConfig (StripeKey key) Nothing
       tokenId = TokenId token
       completeStripeCharge currency' = do
-        result <- stripe config $
+        result <- stripe stripeConfig $
           createCharge (Amount amount) currency'
           -&- tokenId
           -&- MetaData [("Voucher", voucher)]
diff --git a/src/PaymentServer/Server.hs b/src/PaymentServer/Server.hs
index ee07b2c..a789f38 100644
--- a/src/PaymentServer/Server.hs
+++ b/src/PaymentServer/Server.hs
@@ -22,9 +22,13 @@ import Servant
   , (:>)
   , (:<|>)((:<|>))
   )
+
+import Web.Stripe.Client
+  ( StripeConfig
+  )
+
 import PaymentServer.Processors.Stripe
   ( StripeAPI
-  , StripeSecretKey
   , stripeServer
   )
 import PaymentServer.Redemption
@@ -44,9 +48,9 @@ type PaymentServerAPI
   :<|> "v1" :> "redeem" :> RedemptionAPI
 
 -- | Create a server which uses the given database.
-paymentServer :: VoucherDatabase d => StripeSecretKey -> Issuer -> d -> Server PaymentServerAPI
-paymentServer key issuer database =
-  stripeServer key database
+paymentServer :: VoucherDatabase d => StripeConfig -> Issuer -> d -> Server PaymentServerAPI
+paymentServer stripeConfig issuer database =
+  stripeServer stripeConfig database
   :<|> redemptionServer issuer database
 
 paymentServerAPI :: Proxy PaymentServerAPI
@@ -57,13 +61,13 @@ paymentServerAPI = Proxy
 paymentServerApp
   :: VoucherDatabase d
   => [Origin]              -- ^ A list of CORS Origins to accept.
-  -> StripeSecretKey
+  -> StripeConfig
   -> Issuer
   -> d
   -> Application
-paymentServerApp corsOrigins key issuer =
+paymentServerApp corsOrigins stripeConfig issuer =
   let
-    app = serve paymentServerAPI . paymentServer key issuer
+    app = serve paymentServerAPI . paymentServer stripeConfig issuer
     withCredentials = False
     corsResourcePolicy = simpleCorsResourcePolicy
                          { corsOrigins = Just (corsOrigins, withCredentials)
-- 
GitLab