git.iko.soy/typeable/connexpay
on master help sign in
Recognize declined transactions from OK responses (#9)
92e8e52Uladzimir Stsepchanka
connexpay/src/Web/Connexpay/Payments.hs+8−6
unifiedsplit
@@ -5,6 +5,7 @@ module Web.Connexpay.Payments
5 , ExpirationDate(..)
5 , ExpirationDate(..)
6 , Customer(..)
6 , Customer(..)
7 , RiskData(..)
7 , RiskData(..)
8+ , AuthorizeResult(..)
8 , AuthResponse(..)
9 , AuthResponse(..)
9 , TransactionStatus(..)
10 , TransactionStatus(..)
10 , authorisePayment
11 , authorisePayment
@@ -26,12 +27,13 @@ import Web.Connexpay.Types
26
27
27-- | Authorise a credit card payment.
28-- | Authorise a credit card payment.
28authorisePayment
29authorisePayment
29− :: Connexpay -> Env -> AuthRequest -> IO (Response AuthError AuthResponse)
30+ :: Connexpay -> Env -> AuthRequest -> IO (Response AuthError AuthorizeResult)
30−authorisePayment connexpay env raw = guessResponseErrorType guessAuthError <$>
31+authorisePayment connexpay env raw =
32+ bimapResponse (guessErrorType guessAuthError) postProcessAuthResponse <$>
31− doRequest connexpay env "authonlys" RequestBody
33+ doRequest connexpay env "authonlys" RequestBody
32− { raw
34+ { raw
33− , logMasker = maskAuthRequest
35+ , logMasker = maskAuthRequest
34− }
36+ }
35
37
36-- | Void payment
38-- | Void payment
37voidPayment :: Connexpay -> Env -> VoidRequest -> IO (Response () ())
39voidPayment :: Connexpay -> Env -> VoidRequest -> IO (Response () ())
connexpay/src/Web/Connexpay/Payments/Types.hs+27−1
@@ -6,6 +6,7 @@ module Web.Connexpay.Payments.Types where
6import Data.Aeson
6import Data.Aeson
7import Data.Aeson.TH
7import Data.Aeson.TH
8import Data.Fixed
8import Data.Fixed
9+import Data.Function
9import Data.Int (Int32)
10import Data.Int (Int32)
10import Data.Maybe
11import Data.Maybe
11import Data.Text (Text)
12import Data.Text (Text)
@@ -116,6 +117,30 @@ instance ToJSON RiskData where
116maskRiskData :: LogMasker RiskData
117maskRiskData :: LogMasker RiskData
117maskRiskData = id
118maskRiskData = id
118
119
120+data AuthorizeResult
121+ = Authorized AuthResponse
122+ -- ^ Transaction has been created and authorized successfully
123+ | Declined AuthResponse
124+ -- ^ Transaction has been created, but not authorized,
125+ -- e.g. declined by internal Connexpay fraud checks
126+ | Ambiguous AuthResponse
127+ -- ^ We can't interpret the result, e.g. wasProcess=true,
128+ -- but transaction status is not approved.
129+ -- Something for further investigation.
130+ deriving stock (Show)
131+
132+postProcessAuthResponse :: AuthResponse -> AuthorizeResult
133+postProcessAuthResponse resp = resp & case (completed, statusOk) of
134+ (True, True) -> Authorized
135+ (False, False) -> Declined
136+ _ -> Ambiguous
137+ where
138+ -- Connexpay documentation is not very clear about 'wasProcessed' semantics
139+ -- when it is missing, so we are intentionally strict here with the idea
140+ -- to warn about all 'Ambiguous' cases and adjust this code accordingly.
141+ completed = resp.wasProcessed == Just True
142+ statusOk = resp.status `elem` [TransactionApproved, TransactionApprovedWarning]
143+
119data AuthResponse = AuthResponse
144data AuthResponse = AuthResponse
120 { guid :: AuthOnlyGuid
145 { guid :: AuthOnlyGuid
121 , status :: TransactionStatus
146 , status :: TransactionStatus
@@ -123,6 +148,7 @@ data AuthResponse = AuthResponse
123 , processorResponseMessage :: Maybe Text
148 , processorResponseMessage :: Maybe Text
124 , addressVerificationCode :: Maybe Text
149 , addressVerificationCode :: Maybe Text
125 , cvvVerificationCode :: Maybe Text
150 , cvvVerificationCode :: Maybe Text
151+ , wasProcessed :: Maybe Bool
126 } deriving stock (Show)
152 } deriving stock (Show)
127
153
128-- | Transaction status in Connexpay
154-- | Transaction status in Connexpay
@@ -139,7 +165,7 @@ data TransactionStatus
139 -- ^ Processor errored out
165 -- ^ Processor errored out
140 | TransactionOther Text
166 | TransactionOther Text
141 -- ^ In case they return something unexpected
167 -- ^ In case they return something unexpected
142− deriving stock (Show)
168+ deriving stock (Eq, Show)
143
169
144instance FromJSON TransactionStatus where
170instance FromJSON TransactionStatus where
145 parseJSON = withText "TransactionStatus" $ pure . \case
171 parseJSON = withText "TransactionStatus" $ pure . \case
connexpay/src/Web/Connexpay/Types.hs+2−2
@@ -67,8 +67,8 @@ bimapResponse f g = \case
67 BadRequest body -> BadRequest body
67 BadRequest body -> BadRequest body
68 MissingOrExpiredToken -> MissingOrExpiredToken
68 MissingOrExpiredToken -> MissingOrExpiredToken
69
69
70−guessResponseErrorType :: (Error () -> e) -> Response () a -> Response e a
70+guessErrorType :: (Error () -> e) -> Error () -> Error e
71−guessResponseErrorType mkErr = bimapResponse (\e -> e { errorType = mkErr e }) id
71+guessErrorType mkErr e = e { errorType = mkErr e }
72
72
73data Error e = Error
73data Error e = Error
74 { message :: Text
74 { message :: Text