From 2655f77c83b1ebd2479764eabd093e6ba0057403 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Mon, 28 Feb 2022 16:27:30 -0500 Subject: [PATCH] handle the IOException thrown by `payForVoucher` `throwError voucherAlreadyPaid` above is a red herring. That codepath works fine. It's just unreachable because `payForVoucher` never returns `Left AlreadyPaid`. Instead, it does `throwIO AlreadyPaid`. Servant doesn't know how to do anything good with `AlreadyPaid` (a `PaymentError` rather than a `ServerError`) so it logs it and returns a 500 error to the client. This change adds an exception handler to the `payForVoucher` call that transforms all `PaymentError` exceptions into `Left e` values which `charge` is already handling. `throwError` will eventually throw a `ServerError` with correct details filled in for these. --- src/PaymentServer/Processors/Stripe.hs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/PaymentServer/Processors/Stripe.hs b/src/PaymentServer/Processors/Stripe.hs index 4dc1ad9..5745ad6 100644 --- a/src/PaymentServer/Processors/Stripe.hs +++ b/src/PaymentServer/Processors/Stripe.hs @@ -2,6 +2,7 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE ScopedTypeVariables #-} module PaymentServer.Processors.Stripe ( StripeAPI @@ -12,6 +13,9 @@ module PaymentServer.Processors.Stripe , charge ) where +import Control.Exception + ( catch + ) import Control.Monad.IO.Class ( liftIO ) @@ -153,16 +157,25 @@ withSuccessFailureMetrics attemptCount successCount op = do -- and if the Charge is okay, then set the voucher as "paid" in the database. charge :: VoucherDatabase d => StripeConfig -> d -> Charges -> Handler Acknowledgement charge stripeConfig d (Charges token voucher 650 USD) = do - result <- liftIO ((payForVoucher d voucher (completeStripeCharge USD)) :: IO ProcessorResult) + result <- liftIO payForVoucher' case result of Left AlreadyPaid -> throwError voucherAlreadyPaid + Left (PaymentFailed (StripeError { errorType = errorType, errorMsg = msg })) -> do liftIO $ print "Stripe createCharge failed:" liftIO $ print msg throwError . errorForStripeType $ errorType + Right chargeId -> return Ok + where + payForVoucher' :: IO ProcessorResult + payForVoucher' = do + payForVoucher d voucher (completeStripeCharge USD) `catch` ( + \(e :: PaymentError) -> return $ Left e + ) + tokenId = TokenId token completeStripeCharge :: Currency -> IO ProcessorResult completeStripeCharge currency = do -- GitLab