Skip to content
Merged
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
96 changes: 94 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,61 @@ 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)
-- In case if external payout api call failed, we still need to store transferId and transferStatus
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
pure
CreateExternalPayoutResp
{ orderId = req.orderId,
status = FAILURE,
orderType = Just req.orderType,
idAssignedByServiceProvider = Nothing,
amount = req.amount,
customerId = Just req.customerId
}
pure $ mkCreatePayoutOrderResp createTransferResp createExternalPayoutResp
where
mkTransferReq :: Text -> CreatePayoutOrderReq -> CreateTransferReq
mkTransferReq connectedAccountId CreatePayoutOrderReq {..} = do
let senderAccountId = TransferPlatformAccount
destinationAccount = TransferConnectedAccount connectedAccountId
CreateTransferReq
{ amount = transferAmount,
currency,
senderAccountId,
destinationAccount,
description = Just remark
}

mkCreatePayoutOrderResp :: CreateTransferResp -> CreateExternalPayoutResp -> CreatePayoutOrderResp
mkCreatePayoutOrderResp CreateTransferResp {transferId, transferStatus} CreateExternalPayoutResp {..} =
CreatePayoutOrderResp
{ orderId,
status,
transferStatus = Just transferStatus,
orderType,
transferId = Just transferId,
idAssignedByServiceProvider,
udf1 = Nothing,
udf2 = Nothing,
udf3 = Nothing,
udf4 = Nothing,
udf5 = Nothing,
amount,
refunds = Nothing,
payments = Nothing,
fulfillments = Nothing,
customerId
}

payoutOrderStatus ::
( EncFlow m r,
Expand All @@ -50,4 +106,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,
transferStatus = req.transferStatus,
orderType,
transferId = req.transferId,
idAssignedByServiceProvider,
udf1 = Nothing,
udf2 = Nothing,
udf3 = Nothing,
udf4 = Nothing,
udf5 = Nothing,
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
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ createPayoutOrder config req = do
mkCreatePayoutOrderResp Payout.PayoutOrderResp {..} = do
CreatePayoutOrderResp
{ amount = realToFrac amount,
transferStatus = Nothing,
transferId = Nothing,
idAssignedByServiceProvider = Nothing,
..
}
Expand Down Expand Up @@ -146,6 +148,8 @@ payoutOrderStatus config req = do
mkPayoutOrderStatusResp Payout.PayoutOrderResp {..} = do
CreatePayoutOrderResp
{ amount = realToFrac amount,
transferStatus = Nothing,
transferId = Nothing,
idAssignedByServiceProvider = Nothing,
..
}
Expand Down
Loading
Loading