Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/mobility-core/mobility-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ library
Kernel.External.Payout.Stripe.Types
Kernel.External.Payout.Stripe.Types.Common
Kernel.External.Payout.Stripe.Types.Payout
Kernel.External.Payout.Stripe.Types.Transfer
Kernel.External.Payout.Types
Kernel.External.Plasma
Kernel.External.Plasma.Interface
Expand Down
12 changes: 6 additions & 6 deletions lib/mobility-core/src/Kernel/External/Payment/Interface.hs
Original file line number Diff line number Diff line change
Expand Up @@ -450,9 +450,9 @@ createRefund ::
PaymentServiceConfig ->
CreateRefundReq ->
m CreateRefundResp
createRefund config paymentIntentId = case config of
createRefund config req = case config of
JuspayConfig _ -> throwError $ InternalError "Juspay Create Refund not supported."
StripeConfig cfg -> Stripe.createRefund cfg paymentIntentId
StripeConfig cfg -> Stripe.createRefund cfg req
PaytmEDCConfig _ -> throwError $ InternalError "PaytmEDC Create Refund not supported."

getRefund ::
Expand All @@ -464,9 +464,9 @@ getRefund ::
PaymentServiceConfig ->
GetRefundReq ->
m GetRefundResp
getRefund config paymentIntentId = case config of
getRefund config req = case config of
JuspayConfig _ -> throwError $ InternalError "Juspay Get Refund not supported."
StripeConfig cfg -> Stripe.getRefund cfg paymentIntentId
StripeConfig cfg -> Stripe.getRefund cfg req
PaytmEDCConfig _ -> throwError $ InternalError "PaytmEDC Get Refund not supported."

cancelRefund ::
Expand All @@ -478,9 +478,9 @@ cancelRefund ::
PaymentServiceConfig ->
CancelRefundReq ->
m CancelRefundResp
cancelRefund config paymentIntentId = case config of
cancelRefund config req = case config of
JuspayConfig _ -> throwError $ InternalError "Juspay Cancel Refund not supported."
StripeConfig cfg -> Stripe.cancelRefund cfg paymentIntentId
StripeConfig cfg -> Stripe.cancelRefund cfg req
PaytmEDCConfig _ -> throwError $ InternalError "PaytmEDC Cancel Refund not supported."

verifyVPA ::
Expand Down
24 changes: 13 additions & 11 deletions lib/mobility-core/src/Kernel/External/Payment/Interface/Stripe.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Kernel.External.Payment.Interface.Stripe
where

import Control.Applicative ((<|>))
import qualified Data.Text as T
import Data.Time
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import Kernel.External.Encryption
Expand Down Expand Up @@ -325,7 +326,7 @@ createPaymentIntent config req = do
ConnectedAccount -> createConnectedAccountCharge url apiKey req
where
-- Platform Charge: No cloning, no on_behalf_of
createPlatformCharge url apiKey CreatePaymentIntentReq {amount = amonutInUsd, ..} = do
createPlatformCharge url apiKey CreatePaymentIntentReq {amount = amountInUsd, ..} = do
let paymentIntentReq = mkPlatformPaymentIntentReq
paymentIntentResp <- Stripe.createPaymentIntent url apiKey paymentIntentReq
let paymentIntentId = paymentIntentResp.id
Expand All @@ -335,12 +336,13 @@ createPaymentIntent config req = do
where
mkPlatformPaymentIntentReq :: Stripe.PaymentIntentReq
mkPlatformPaymentIntentReq =
let application_fee_amount = eurToCents applicationFeeAmount
amountInCents = eurToCents amonutInUsd
let amountInCents = eurToCents amountInUsd
payment_method = paymentMethod -- Use original payment method (NO cloning)
receipt_email = receiptEmail
on_behalf_of = Nothing -- OMIT for platform charges
transfer_data = Stripe.TransferData {destination = driverAccountId}
(transfer_data, application_fee_amount) = case config.transferPaymentToConnectedAccount of
Just False -> (Nothing, Nothing)
_ -> (Just $ Stripe.TransferData {destination = driverAccountId}, Just $ eurToCents applicationFeeAmount) -- True is default
confirm = True
description = Nothing
setup_future_usage = Just Stripe.FutureUsageOffSession -- off_session: enables SCA exemption for saved cards
Expand All @@ -365,13 +367,13 @@ createPaymentIntent config req = do
return $ CreatePaymentIntentResp {..}
where
mkPaymentIntentReq :: PaymentMethodId -> CreatePaymentIntentReq -> Stripe.PaymentIntentReq
mkPaymentIntentReq clonedPaymentMethodId CreatePaymentIntentReq {amount = amonutInUsd, ..} = do
let application_fee_amount = usdToCents applicationFeeAmount
let amountInCents = usdToCents amonutInUsd
mkPaymentIntentReq clonedPaymentMethodId CreatePaymentIntentReq {amount = amountInUsd, ..} = do
let application_fee_amount = Just $ usdToCents applicationFeeAmount
let amountInCents = usdToCents amountInUsd
let payment_method = clonedPaymentMethodId
let receipt_email = receiptEmail
let on_behalf_of = Just driverAccountId
let transfer_data = Stripe.TransferData {destination = driverAccountId}
let transfer_data = Just $ Stripe.TransferData {destination = driverAccountId}
let confirm = True
let description = Nothing
let setup_future_usage = Just Stripe.FutureUsageOffSession -- off_session: enables SCA exemption for saved cards
Expand Down Expand Up @@ -739,10 +741,10 @@ createRefund config req = do
mkRefundResp <$> Stripe.createRefund url apiKey (Just req.driverAccountId) refundReq

mkRefundReq :: CreateRefundReq -> Maybe Bool -> Stripe.RefundReq
mkRefundReq CreateRefundReq {amount = amonutInUsd, ..} reverse_transfer =
mkRefundReq CreateRefundReq {amount = amountInUsd, ..} reverse_transfer =
let charge = Nothing
payment_intent = Just req.paymentIntentId
amountInCents = eurToCents <$> amonutInUsd
amountInCents = eurToCents <$> amountInUsd
metadata = Metadata {order_short_id = Just orderShortId, order_id = Just orderId, refunds_id = Just refundsId}
refund_application_fee = Just req.refundApplicationFee
instructions_email = req.email
Expand Down Expand Up @@ -802,7 +804,7 @@ mkGetRefundResp Stripe.RefundObject {..} =
refundsId = metadata >>= (.refunds_id),
paymentIntentId = payment_intent,
amount = centsToUsd amount,
currency,
currency = readMaybe . T.unpack $ T.toUpper currency,
status = castRefundStatus status,
reverseTransferId = transfer_reversal,
errorCode = failure_reason
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -924,7 +924,7 @@ data GetRefundResp = GetRefundResp
refundsId :: Maybe Text,
paymentIntentId :: Maybe PaymentIntentId,
amount :: HighPrecMoney,
currency :: Currency,
currency :: Maybe Currency,
status :: RefundStatus,
reverseTransferId :: Maybe Text,
errorCode :: Maybe Text
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ data StripeCfg = StripeCfg
url :: BaseUrl,
businessProfile :: Maybe BusinessProfile,
chargeDestination :: ChargeDestination,
transferPaymentToConnectedAccount :: Maybe Bool,
webhookEndpointSecret :: Maybe (EncryptedField 'AsEncrypted Text),
webhookToleranceSeconds :: Maybe Seconds,
serviceMode :: Maybe ServiceMode,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ data PaymentIntentReq = PaymentIntentReq
payment_method :: PaymentMethodId,
receipt_email :: Maybe Text,
setup_future_usage :: Maybe SetupFutureUsage,
application_fee_amount :: Int,
application_fee_amount :: Maybe Int,
capture_method :: CaptureMethod,
confirmation_method :: ConfirmationMethod,
on_behalf_of :: Maybe AccountId,
use_stripe_sdk :: Bool,
return_url :: Text,
transfer_data :: TransferData
transfer_data :: Maybe TransferData
}
deriving stock (Show, Eq, Generic, Read)
deriving anyclass (FromJSON, ToJSON, ToSchema)
Expand All @@ -62,9 +62,9 @@ instance ToForm PaymentIntentReq where
("capture_method", [toQueryParam capture_method]),
("confirmation_method", [toQueryParam confirmation_method]),
("use_stripe_sdk", [toQueryParam use_stripe_sdk]),
("return_url", [toQueryParam return_url]),
("transfer_data[destination]", [toQueryParam transfer_data.destination])
("return_url", [toQueryParam return_url])
]
<> maybeToForm "transfer_data[destination]" (transfer_data <&> (.destination))
<> maybeToForm "metadata[order_short_id]" metadata.order_short_id
<> maybeToForm "description" description
<> maybeToForm "receipt_email" receipt_email
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Data.OpenApi (ToSchema (declareNamedSchema), genericDeclareNamedSchema)
import Kernel.External.Payment.Stripe.Types.Common
import Kernel.Prelude
import Kernel.Types.HideSecrets
import Kernel.Types.Price (Currency)
import Kernel.Utils.JSON
import qualified Kernel.Utils.Schema as S
import Web.FormUrlEncoded
Expand Down Expand Up @@ -56,7 +55,7 @@ data RefundObject = RefundObject
balance_transaction :: Maybe Text, -- Balance transaction that describes the impact of this refund on your account balance
charge :: Maybe Text, -- ID of the charge that was refunded
created :: Int, -- Time at which the refund was created
currency :: Currency, -- Currency of the refund
currency :: Text, -- Currency of the refund
metadata :: Maybe Metadata, -- Set of key-value pairs attached to the refund
payment_intent :: Maybe Text, -- ID of the PaymentIntent that was refunded
reason :: Maybe RefundReason, -- Reason for the refund
Expand Down
105 changes: 103 additions & 2 deletions lib/mobility-core/src/Kernel/External/Payout/Interface.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ module Kernel.External.Payout.Interface
)
where

import qualified Kernel.External.Payment.Interface as Payment
import qualified Kernel.External.Payout.Interface.Juspay as Juspay
import qualified Kernel.External.Payout.Interface.Stripe as Stripe
import Kernel.External.Payout.Interface.Types as Reexport
import Kernel.External.Payout.Types as Reexport
import Kernel.Prelude
import Kernel.Tools.Metrics.CoreMetrics (CoreMetrics)
import Kernel.Types.Error
import Kernel.Utils.Common

createPayoutOrder ::
Expand All @@ -37,7 +39,70 @@ createPayoutOrder ::
m CreatePayoutOrderResp
createPayoutOrder serviceConfig req = case serviceConfig of
JuspayConfig cfg -> Juspay.createPayoutOrder cfg req
StripeConfig cfg -> Stripe.createPayoutOrder cfg req
StripeConfig cfg -> do
connectedAccountId <- req.mConnectedAccountId & fromMaybeM (InvalidRequest "connectedAccountId required for Stripe payout")
createTransferResp <- Stripe.createTransfer cfg (mkTransferReq connectedAccountId req)

result <- withTryCatch "createExternalPayout" $ Stripe.createExternalPayout cfg req
createExternalPayoutResp <- case result of
Right resp -> pure resp
Left e -> do
let err = fromException @Payment.StripeError e
errorCode = err <&> toErrorCode
errorMessage = err >>= toMessage
logError $ "Error while create external payout : " <> show err <> "error code : " <> show errorCode <> "error message : " <> show errorMessage <> " orderId: " <> req.orderId
Comment on lines +49 to +53

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid logging full Stripe error payloads — risk of leaking PII / sensitive request data.

logError $ "Error while create external payout : " <> show err <> "error code : " <> show errorCode <> "error message : " <> show errorMessage <> " orderId: " <> req.orderId interpolates the entire StripeError exception via show. Stripe error responses can contain echoed request fields (customer email, name, bank/card identifiers, descriptions) and sometimes PII from the request. Logging the structured errorCode + errorMessage is appropriate; logging the full exception is not.

Also, there's no separator between adjacent string fragments (...show err <> "error code : "...) — the resulting log line will read ...error code : ... jammed together.

🛡️ Proposed log-cleanup
-        logError $ "Error while create external payout : " <> show err <> "error code : " <> show errorCode <> "error message : " <> show errorMessage <> " orderId: " <> req.orderId
+        logError $
+          "Error while creating external payout"
+            <> " errorCode: " <> show errorCode
+            <> " errorMessage: " <> show errorMessage
+            <> " orderId: " <> req.orderId
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Payout/Interface.hs` around lines 51 -
55, The code logs the entire StripeError via show (variable err) which may
contain PII; update the log to avoid printing the full exception and instead log
only the sanitized fields already extracted (errorCode and errorMessage) and the
orderId, and fix missing separators. Replace the existing logError call that
references err with a single formatted message like "Error while creating
external payout: code=<...> message=<...> orderId=<...>" using errorCode and
errorMessage (handle their Maybe values with sensible defaults such as
"unknown"), keep Payment.StripeError/err extraction as-is but do not include
show err anywhere, and ensure spaces/separators are present between fragments.

pure
CreateExternalPayoutResp
{ orderId = req.orderId,
externalPayoutStatus = EXTERNAL_PAYOUT_FAILED,
orderType = Just req.orderType,
idAssignedByServiceProvider = Nothing,
externalPayoutAmount = req.externalPayoutAmount,
customerId = Just req.customerId
}
pure $ mkCreatePayoutOrderResp createTransferResp createExternalPayoutResp
Comment on lines +42 to +63

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🌐 Web query:

Stripe Idempotency-Key header /v1/transfers double charge prevention

💡 Result:

Yes, the Stripe Idempotency-Key header prevents double transfers (or "double charges") on POST /v1/transfers by making the request idempotent. Stripe's API supports idempotency keys on all POST endpoints, including transfers/create, which is a mutating POST request to create a transfer object.[1][2][3][4] This allows safe retries: generate a unique Idempotency-Key (e.g., UUIDv4, up to 255 chars), include it in the header, and if a network error occurs, retry with the same key. Stripe caches the first response (success or failure) for 24 hours; subsequent identical requests return the cached result without duplicating the transfer.[1][2][3][4] Example curl for /v1/transfers with Idempotency-Key (adapted from customer example, as transfers supports the same): curl https://api.stripe.com/v1/transfers \ -u sk_test_...: \ -H "Idempotency-Key: example-uuid-v4-key" \ -d amount=1000 \ -d currency=usd \ -d destination=acct_...[1][3][5] In client libraries, pass idempotencyKey in options (e.g., Node: {idempotencyKey: 'key'}).[1][2] Keys expire after 24 hours; reuse generates a new request if params differ (errors if not matching).[1][2] Use for all mutating POSTs to prevent duplicates from retries.[3][4]

Citations:


🏁 Script executed:

# Find files related to Stripe transfer handling
find . -name "*.hs" -type f | xargs grep -l "TransferReq\|createTransfer" | head -20

Repository: nammayatri/shared-kernel

Length of output: 395


🏁 Script executed:

# Look for the Stripe.hs or Stripe/Flow.hs mentioned in the review
fd -e hs "Stripe" | grep -E "(Stripe.hs|Flow.hs)" | head -20

Repository: nammayatri/shared-kernel

Length of output: 199


🏁 Script executed:

# Search for the Interface.hs file mentioned
fd -e hs "Interface.hs" | head -10

Repository: nammayatri/shared-kernel

Length of output: 707


🏁 Script executed:

# Examine TransferReq type definition
cat -n lib/mobility-core/src/Kernel/External/Payout/Stripe/Types/Transfer.hs

Repository: nammayatri/shared-kernel

Length of output: 2318


🏁 Script executed:

# Look at createTransfer implementation
cat -n lib/mobility-core/src/Kernel/External/Payout/Stripe/Flow.hs

Repository: nammayatri/shared-kernel

Length of output: 5128


🏁 Script executed:

# Search for the TODO comment with "retrie" typo
grep -r "retrie" lib/mobility-core/src/Kernel/External/Payout/ --include="*.hs"

Repository: nammayatri/shared-kernel

Length of output: 188


🏁 Script executed:

# Search for logging patterns in the payout interface
grep -n "logError\|log" lib/mobility-core/src/Kernel/External/Payout/Interface.hs

Repository: nammayatri/shared-kernel

Length of output: 253


🏁 Script executed:

# Read the full Interface.hs file to verify the concerns
wc -l lib/mobility-core/src/Kernel/External/Payout/Interface.hs

Repository: nammayatri/shared-kernel

Length of output: 130


🏁 Script executed:

# Read the relevant portions of Interface.hs
sed -n '1,100p' lib/mobility-core/src/Kernel/External/Payout/Interface.hs

Repository: nammayatri/shared-kernel

Length of output: 4241


🏁 Script executed:

# Check around line 43-65 and beyond to see the full context
sed -n '40,120p' lib/mobility-core/src/Kernel/External/Payout/Interface.hs

Repository: nammayatri/shared-kernel

Length of output: 3397


🏁 Script executed:

# Search for other uses of createTransfer to see if idempotency is handled at a higher level
grep -n "createTransfer" lib/mobility-core/src/Kernel/External/Payout/Interface.hs

Repository: nammayatri/shared-kernel

Length of output: 369


🏁 Script executed:

# Check if there's an idempotency key or similar in the request context
grep -r "IdempotencyKey\|idempotency" lib/mobility-core/src/Kernel/External/Payout/ --include="*.hs"

Repository: nammayatri/shared-kernel

Length of output: 50


🏁 Script executed:

# Check Stripe.Interface to see if idempotency is handled there
cat -n lib/mobility-core/src/Kernel/External/Payout/Interface/Stripe.hs

Repository: nammayatri/shared-kernel

Length of output: 5164


🏁 Script executed:

# Look at CreateTransferReq to see if it differs from TransferReq
grep -A 20 "data CreateTransferReq" lib/mobility-core/src/Kernel/External/Payout/ -r --include="*.hs"

Repository: nammayatri/shared-kernel

Length of output: 1069


🏁 Script executed:

# Check what StripeError contains to assess PII leak risk
grep -A 10 "data StripeError\|newtype StripeError" lib/mobility-core/src/Kernel/External/Payment/ -r --include="*.hs" | head -40

Repository: nammayatri/shared-kernel

Length of output: 4417


Critical: missing idempotency safeguard on createTransfer exposes risk of double charges on retry.

The flow is: (1) call Stripe.createTransfer (committing money), (2) call Stripe.createExternalPayout. If the caller retries createPayoutOrder (e.g., because of a transient timeout in step 2, or any client-side failure after step 1 succeeded), step 1 will execute again and create a second Stripe transfer for the same orderId — double-funding the connected account. The IMPORTANT comment documents the intent but provides no actual safeguard.

Stripe's recommended fix is to send an Idempotency-Key header on /v1/transfers (typically derived from req.orderId). The TransferReq type and CreateTransferAPI definition currently lack any idempotency key support. Add an idempotencyKey :: Text field to TransferReq, wire it through the API definition as an Idempotency-Key header, and populate it (e.g., from orderId or a request-scoped identifier) in the Stripe interface layer.

Minor: fix TODO typo at line 30 — "retrie" should be "retry".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/mobility-core/src/Kernel/External/Payout/Interface.hs` around lines 43 -
65, The createTransfer call lacks idempotency which can double-charge on
retries; add an idempotencyKey :: Text field to the TransferReq type, update the
CreateTransferAPI to send that field as the "Idempotency-Key" header, and modify
mkTransferReq (and the call site in StripeConfig branch where mkTransferReq is
used) to populate idempotencyKey from req.orderId (or a request-scoped id)
before calling Stripe.createTransfer; also correct the TODO typo "retrie" to
"retry". Ensure all uses of TransferReq and CreateTransferAPI are updated to
accept and forward the new idempotencyKey.

where
mkTransferReq :: Text -> CreatePayoutOrderReq -> CreateTransferReq
mkTransferReq connectedAccountId CreatePayoutOrderReq {..} = do
let senderAccountId = TransferPlatformAccount
destinationAccount = TransferConnectedAccount connectedAccountId
CreateTransferReq
{ amount,
currency,
senderAccountId,
destinationAccount,
description = Just remark
}

mkCreatePayoutOrderResp :: CreateTransferResp -> CreateExternalPayoutResp -> CreatePayoutOrderResp
mkCreatePayoutOrderResp CreateTransferResp {transferId} CreateExternalPayoutResp {..} =
CreatePayoutOrderResp
{ orderId,
status = castExternalPayoutStatusToStatus externalPayoutStatus,
externalPayoutStatus = Just externalPayoutStatus,
orderType,
transferId = Just transferId,
idAssignedByServiceProvider,
udf1 = Nothing,
udf2 = Nothing,
udf3 = Nothing,
udf4 = Nothing,
udf5 = Nothing,
amount = req.amount,
refunds = Nothing,
payments = Nothing,
fulfillments = Nothing,
customerId
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

-- IMPORTANT: always consider TRANSFERRED or SUCCESS if transfer was successfull, to avoid multiple charges
castExternalPayoutStatusToStatus :: ExternalPayoutStatus -> PayoutOrderStatus
castExternalPayoutStatusToStatus = \case
EXTERNAL_PAYOUT_PAID -> SUCCESS
EXTERNAL_PAYOUT_PENDING -> TRANSFERRED
EXTERNAL_PAYOUT_IN_TRANSIT -> TRANSFERRED
EXTERNAL_PAYOUT_FAILED -> TRANSFERRED
EXTERNAL_PAYOUT_CANCELED -> TRANSFERRED

payoutOrderStatus ::
( EncFlow m r,
Expand All @@ -50,4 +115,40 @@ payoutOrderStatus ::
m PayoutOrderStatusResp
payoutOrderStatus serviceConfig req = case serviceConfig of
JuspayConfig cfg -> Juspay.payoutOrderStatus cfg req
StripeConfig cfg -> Stripe.payoutOrderStatus cfg req
StripeConfig cfg -> do
resp <- Stripe.externalPayoutOrderStatus cfg req
pure $ mkPayoutOrderStatusResp resp
where
mkPayoutOrderStatusResp :: ExternalPayoutOrderStatusResp -> PayoutOrderStatusResp
mkPayoutOrderStatusResp CreateExternalPayoutResp {..} =
CreatePayoutOrderResp
{ orderId,
status = if isJust req.transferId then castExternalPayoutStatusToStatus externalPayoutStatus else req.currentStatus, -- extra check for req.transferId
externalPayoutStatus = Just externalPayoutStatus,
orderType,
transferId = req.transferId,
idAssignedByServiceProvider,
udf1 = Nothing,
udf2 = Nothing,
udf3 = Nothing,
udf4 = Nothing,
udf5 = Nothing,
amount = req.amount,
refunds = Nothing,
payments = Nothing,
fulfillments = Nothing,
customerId
}

createTransfer ::
( CoreMetrics m,
EncFlow m r,
HasRequestId r,
MonadReader r m
) =>
PayoutServiceConfig ->
CreateTransferReq ->
m CreateTransferResp
createTransfer config req = case config of
JuspayConfig _ -> throwError $ InternalError "Juspay Create Transfer not supported."
StripeConfig cfg -> Stripe.createTransfer cfg req
32 changes: 30 additions & 2 deletions lib/mobility-core/src/Kernel/External/Payout/Interface/Juspay.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import qualified Kernel.External.Payout.Juspay.Types.Payout as Payout
import qualified Kernel.External.Payout.Juspay.Webhook as Juspay
import Kernel.Prelude
import qualified Kernel.Tools.Metrics.CoreMetrics as Metrics
import Kernel.Types.Common
import Kernel.Types.Common hiding (LogLevel (ERROR))
import Kernel.Types.Error
import Kernel.Types.Field
import Kernel.Utils.Error.Throwing (fromMaybeM)
Expand Down Expand Up @@ -113,7 +113,10 @@ createPayoutOrder config req = do
mkCreatePayoutOrderResp Payout.PayoutOrderResp {..} = do
CreatePayoutOrderResp
{ amount = realToFrac amount,
externalPayoutStatus = Nothing,
transferId = Nothing,
idAssignedByServiceProvider = Nothing,
status = castPayoutOrderStatus status,
..
}

Expand Down Expand Up @@ -146,7 +149,10 @@ payoutOrderStatus config req = do
mkPayoutOrderStatusResp Payout.PayoutOrderResp {..} = do
CreatePayoutOrderResp
{ amount = realToFrac amount,
externalPayoutStatus = Nothing,
transferId = Nothing,
idAssignedByServiceProvider = Nothing,
status = castPayoutOrderStatus status,
..
}

Expand All @@ -171,7 +177,7 @@ mkWebhookOrderStatusPayoutResp payoutReq = case payoutReq.label of
OrderStatusPayoutResp
{ payoutOrderId = orderId,
idAssignedByServiceProvider = Nothing,
payoutStatus = payoutReq.info.status,
payoutStatus = castPayoutOrderStatus payoutReq.info.status,
orderType = payoutReq.info._type,
merchantCustomerId = payoutReq.info.merchantCustomerId,
amount = realToFrac payoutReq.info.amount,
Expand All @@ -180,3 +186,25 @@ mkWebhookOrderStatusPayoutResp payoutReq = case payoutReq.label of
}
_ -> BadStatusResp
_ -> BadStatusResp

castPayoutOrderStatus :: Juspay.PayoutOrderStatus -> PayoutOrderStatus
castPayoutOrderStatus status = case status of
Juspay.READY_FOR_FULFILLMENT -> READY_FOR_FULFILLMENT
Juspay.FULFILLMENTS_SCHEDULED -> FULFILLMENTS_SCHEDULED
Juspay.FULFILLMENTS_FAILURE -> FULFILLMENTS_FAILURE
Juspay.FULFILLMENTS_SUCCESSFUL -> FULFILLMENTS_SUCCESSFUL
Juspay.FULFILLMENTS_CANCELLED -> FULFILLMENTS_CANCELLED
Juspay.FULFILLMENTS_MANUAL_REVIEW -> FULFILLMENTS_MANUAL_REVIEW
Juspay.FULFILLED_PARTIALLY -> FULFILLED_PARTIALLY
Juspay.INITIATED -> INITIATED
Juspay.FAILURE -> FAILURE
Juspay.SUCCESS -> SUCCESS
Juspay.DISCARDED -> DISCARDED
Juspay.MANUAL_REVIEW -> MANUAL_REVIEW
Juspay.CANCELLED -> CANCELLED
Juspay.GATEWAY_SWITCHED -> GATEWAY_SWITCHED
Juspay.ERROR -> ERROR
Juspay.INVALID -> INVALID
Juspay.VALID -> VALID
Juspay.CONFLICTED -> CONFLICTED
Juspay.REVERSED -> REVERSED
Loading
Loading