@@ -13,6 +13,7 @@ import Data.Aeson ((.:))
13import qualified Data.Aeson as Json
13import qualified Data.Aeson as Json
14import qualified Data.ByteString as BS
14import qualified Data.ByteString as BS
15import qualified Data.ByteString.Builder as B
15import qualified Data.ByteString.Builder as B
16+import qualified Data.ByteString.Base64 as Base64
16import qualified Data.ByteString.Char8 as BSC
17import qualified Data.ByteString.Char8 as BSC
17import qualified Data.ByteString.Lazy as LBS
18import qualified Data.ByteString.Lazy as LBS
18import qualified Data.Text as T
19import qualified Data.Text as T
@@ -41,13 +42,17 @@ allowedOrigins =
41-- GET MANAGER
42-- GET MANAGER
44−newtype Manager =
45+data Manager =
45− Manager Http.Manager
47+ { _manager :: Http.Manager
48+ , _authToken :: BS.ByteString
48−getManager :: IO Manager
52+getManager :: String -> IO Manager
49−getManager =
53+getManager secret =
50− Manager <$> Http.newManager Http.tlsManagerSettings
54+ do manager <- Http.newManager Http.tlsManagerSettings
55+ return (Manager manager ("Basic " <> Base64.encode (BSC.pack secret)))
@@ -55,7 +60,7 @@ getManager =
57endpoint :: Manager -> Snap ()
62endpoint :: Manager -> Snap ()
58−endpoint (Manager manager) =
63+endpoint manager =
59 Cors.allow POST allowedOrigins $
64 Cors.allow POST allowedOrigins $
60 do amount <- requireParameter "amount" toAmount
65 do amount <- requireParameter "amount" toAmount
61 frequency <- requireParameter "frequency" toFrequency
66 frequency <- requireParameter "frequency" toFrequency
@@ -102,16 +107,31 @@ newtype StripeCheckoutSession =
102 StripeCheckoutSession { _id :: T.Text }
107 StripeCheckoutSession { _id :: T.Text }
105−getStripeCheckoutSessionID :: Http.Manager -> Int -> IO (Maybe StripeCheckoutSession)
110+getStripeCheckoutSessionID :: Manager -> Int -> IO (Maybe StripeCheckoutSession)
106−getStripeCheckoutSessionID manager amount =
111+getStripeCheckoutSessionID (Manager manager authToken) amount =
107 E.handle handleSomeException $
112 E.handle handleSomeException $
108 do req0 <- Http.parseRequest "https://api.stripe.com/v1/checkout/sessions"
113 do req0 <- Http.parseRequest "https://api.stripe.com/v1/checkout/sessions"
109− req1 <- Multi.formDataBody (toOneTimeParts amount) $ req0 { Http.method = Http.methodPost }
114+ req1 <- Multi.formDataBody (toOneTimeParts amount) (configureRequest authToken req0)
110 Http.withResponse req1 manager $ \response ->
115 Http.withResponse req1 manager $ \response ->
111 do chunks <- Http.brConsume (Http.responseBody response)
116 do chunks <- Http.brConsume (Http.responseBody response)
112 return $ Json.decode $ LBS.fromChunks chunks
117 return $ Json.decode $ LBS.fromChunks chunks
120+-- The "Authorization" header is set based on combining these instructions:
122+-- https://stripe.com/docs/payments/checkout/one-time
123+-- https://stackoverflow.com/a/35442984
125+-- Setting the -u flag appears to add a base64 encoded "Authorization" header.
127+configureRequest :: BS.ByteString -> Http.Request -> Http.Request
128+configureRequest authToken req =
130+ { Http.method = Http.methodPost
131+ , Http.requestHeaders = ("Authorization", authToken) : Http.requestHeaders req
115toOneTimeParts :: Int -> [Multi.Part]
135toOneTimeParts :: Int -> [Multi.Part]
116toOneTimeParts amount =
136toOneTimeParts amount =
117 [ Multi.partBS "payment_method_types[]" "card"
137 [ Multi.partBS "payment_method_types[]" "card"