From 33a7e434515a9a6386bf6918fb33c9bc1d573945 Mon Sep 17 00:00:00 2001 From: Utkarsh Pandey Date: Wed, 4 Sep 2024 17:24:56 +0530 Subject: [PATCH] backend/feat/split_settlement --- .../External/Payment/Interface/Juspay.hs | 38 ++++++++++++-- .../External/Payment/Interface/Types.hs | 52 +++++++++++++++++-- .../External/Payment/Juspay/Types/Common.hs | 20 ++++++- .../Payment/Juspay/Types/CreateOrder.hs | 50 +++++++++++++++++- .../External/Payment/Juspay/Types/Mandate.hs | 7 ++- 5 files changed, 157 insertions(+), 10 deletions(-) diff --git a/lib/mobility-core/src/Kernel/External/Payment/Interface/Juspay.hs b/lib/mobility-core/src/Kernel/External/Payment/Interface/Juspay.hs index f49819401..e38d2e0e6 100644 --- a/lib/mobility-core/src/Kernel/External/Payment/Interface/Juspay.hs +++ b/lib/mobility-core/src/Kernel/External/Payment/Interface/Juspay.hs @@ -193,9 +193,22 @@ mkCreateOrderReq returnUrl clientId CreateOrderReq {..} = mandate_end_date = mandateEndDate, options_get_upi_deep_links = optionsGetUpiDeepLinks, metadata_expiry_in_mins = metadataExpiryInMins, - metadata_gateway_reference_id = metadataGatewayReferenceId + metadata_gateway_reference_id = metadataGatewayReferenceId, + split_settlement_details = mkSplitSettlementDetails <$> splitSettlementDetails } +mkSplitSettlementDetails :: SplitSettlementDetails -> Juspay.SplitSettlementDetails +mkSplitSettlementDetails splitDetails = + Juspay.SplitSettlementDetails + { marketplace = mkMarketplace splitDetails.marketplace, + mdr_borne_by = show splitDetails.mdrBorneBy, + vendor = mkVendor splitDetails.vendor + } + where + mkMarketplace Marketplace {..} = Juspay.Marketplace {..} + mkVendor vendor = Juspay.Vendor {split = mkSplit <$> vendor.split} + mkSplit split = Juspay.Split {amount = split.amount, merchant_commission = split.merchantCommission, sub_mid = split.subMid} + orderStatus :: ( HasCallStack, Metrics.CoreMetrics m, @@ -265,6 +278,7 @@ mkOrderStatusResp Juspay.OrderData {..} = amountRefunded = realToFrac <$> amount_refunded, payerVpa = payer_vpa, upi = castUpi <$> upi, + splitSettlementResponse = mkSplitSettlementResponse <$> split_settlement_response, .. } @@ -286,7 +300,7 @@ mkExecutionReq MandateExecutionReq {..} merchantId = { merchantId, mandateId = mandateId, mandate = Juspay.MandateInfo {notificationId = notificationId, executionDate = show $ utcTimeToPOSIXSeconds executionDate}, - order = Juspay.MandateOrder {orderId = orderId, orderAmount = show amount, orderCustomerId = customerId}, + order = Juspay.MandateOrder {orderId = orderId, orderAmount = show amount, orderCustomerId = customerId, splitSettlementDetails = mkSplitSettlementDetails <$> splitSettlementDetails}, format = "json" } @@ -384,7 +398,7 @@ mkWebhookOrderStatusResp now (eventName, Juspay.OrderAndNotificationStatusConten payerVpa = justOrder.payer_vpa, upi = castUpi <$> justOrder.upi, refunds = maybe [] mkRefundsData justOrder.refunds, - amountRefunded = realToFrac <$> justOrder.amount_refunded + amountRefunded = realToFrac <$> justOrder.amount_refunded -- not adding split } Nothing -> do let (isRetriedOrder, retargetPaymentLink, retargetPaymentLinkExpiry, isRetargetedOrder) = parseRetargetAndRetryData justOrder.metadata justOrder.links justOrder.additional_info @@ -409,6 +423,7 @@ mkWebhookOrderStatusResp now (eventName, Juspay.OrderAndNotificationStatusConten amountRefunded = realToFrac <$> justOrder.amount_refunded, payerVpa = justOrder.payer_vpa, upi = castUpi <$> justOrder.upi, + splitSettlementResponse = mkSplitSettlementResponse <$> justOrder.split_settlement_response, .. } (Nothing, Just justMandate, _, _) -> @@ -458,10 +473,27 @@ mkWebhookOrderStatusResp now (eventName, Juspay.OrderAndNotificationStatusConten amountRefunded = Nothing, payerVpa = justTransaction.payer_vpa, upi = castUpi <$> justTransaction.upi, + splitSettlementResponse = Nothing, .. } (_, _, Nothing, _) -> BadStatusResp +mkSplitSettlementResponse :: Juspay.SplitSettlementResponse -> SplitSettlementResponse +mkSplitSettlementResponse Juspay.SplitSettlementResponse {..} = + SplitSettlementResponse + { splitDetails = split_details >>= (Just . map mkSplitDetailsResponse), + splitApplied = split_applied + } + where + mkSplitDetailsResponse Juspay.SplitDetailsResponse {..} = + SplitDetailsResponse + { subVendorId = sub_vendor_id, + merchantCommission = merchant_commission, + amount = amount, + gatewaySubAccountId = gateway_sub_account_id, + epgTxnId = epg_txn_id + } + castSourceInfo :: Juspay.SourceInfo -> SourceInfo castSourceInfo source_info = SourceInfo diff --git a/lib/mobility-core/src/Kernel/External/Payment/Interface/Types.hs b/lib/mobility-core/src/Kernel/External/Payment/Interface/Types.hs index 9a41ad13e..a2b22f1ce 100644 --- a/lib/mobility-core/src/Kernel/External/Payment/Interface/Types.hs +++ b/lib/mobility-core/src/Kernel/External/Payment/Interface/Types.hs @@ -71,9 +71,36 @@ data CreateOrderReq = CreateOrderReq mandateEndDate :: Maybe Text, metadataGatewayReferenceId :: Maybe Text, optionsGetUpiDeepLinks :: Maybe Bool, - metadataExpiryInMins :: Maybe Int + metadataExpiryInMins :: Maybe Int, + splitSettlementDetails :: Maybe SplitSettlementDetails } +data Split = Split + { amount :: HighPrecMoney, + merchantCommission :: HighPrecMoney, + subMid :: Text + } + deriving stock (Show, Eq, Generic) + +newtype Vendor = Vendor + { split :: [Split] + } + deriving stock (Show, Eq, Generic) + +data SplitSettlementDetails = SplitSettlementDetails + { marketplace :: Marketplace, + mdrBorneBy :: MBY, + vendor :: Vendor + } + deriving stock (Show, Eq, Generic) + +data MBY = MARKETPLACE | VENDOR | ALL deriving stock (Show, Eq, Generic) + +newtype Marketplace = Marketplace + { amount :: HighPrecMoney + } + deriving stock (Show, Eq, Generic) + newtype OrderStatusReq = OrderStatusReq { orderShortId :: Text } @@ -103,7 +130,8 @@ data OrderStatusResp amountRefunded :: Maybe HighPrecMoney, refunds :: [RefundsData], payerVpa :: Maybe Text, - upi :: Maybe Upi + upi :: Maybe Upi, + splitSettlementResponse :: Maybe SplitSettlementResponse } | MandateOrderStatusResp { eventName :: Maybe PaymentStatus, @@ -159,6 +187,23 @@ data OrderStatusResp deriving stock (Show, Read, Eq, Generic) deriving anyclass (FromJSON, ToJSON, ToSchema) +data SplitSettlementResponse = SplitSettlementResponse + { splitDetails :: Maybe [SplitDetailsResponse], + splitApplied :: Maybe Bool + } + deriving stock (Show, Read, Eq, Generic) + deriving anyclass (FromJSON, ToJSON, ToSchema) + +data SplitDetailsResponse = SplitDetailsResponse + { subVendorId :: Maybe Text, + amount :: Maybe HighPrecMoney, + merchantCommission :: Maybe HighPrecMoney, + gatewaySubAccountId :: Maybe Text, + epgTxnId :: Maybe Text + } + deriving stock (Show, Read, Eq, Generic) + deriving anyclass (FromJSON, ToJSON, ToSchema) + data Upi = Upi { payerApp :: Maybe Text, payerVpa :: Maybe Text, @@ -246,7 +291,8 @@ data MandateExecutionReq = MandateExecutionReq amount :: HighPrecMoney, customerId :: Text, mandateId :: Text, - executionDate :: UTCTime + executionDate :: UTCTime, + splitSettlementDetails :: Maybe SplitSettlementDetails } data MandateExecutionRes = MandateExecutionRes diff --git a/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Common.hs b/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Common.hs index 9126b2149..f4f5a13b5 100644 --- a/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Common.hs +++ b/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Common.hs @@ -158,7 +158,25 @@ data OrderData = OrderData additional_info :: Maybe AdditionalInfo, links :: Maybe LinkData, amount_refunded :: Maybe Double, - refunds :: Maybe [RefundsData] + refunds :: Maybe [RefundsData], + split_settlement_response :: Maybe SplitSettlementResponse + } + deriving stock (Show, Generic) + deriving anyclass (FromJSON, ToJSON, ToSchema) + +data SplitSettlementResponse = SplitSettlementResponse + { split_details :: Maybe [SplitDetailsResponse], + split_applied :: Maybe Bool + } + deriving stock (Show, Generic) + deriving anyclass (FromJSON, ToJSON, ToSchema) + +data SplitDetailsResponse = SplitDetailsResponse + { sub_vendor_id :: Maybe Text, + amount :: Maybe HighPrecMoney, + merchant_commission :: Maybe HighPrecMoney, + gateway_sub_account_id :: Maybe Text, + epg_txn_id :: Maybe Text } deriving stock (Show, Generic) deriving anyclass (FromJSON, ToJSON, ToSchema) diff --git a/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/CreateOrder.hs b/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/CreateOrder.hs index 9bdf6b9c0..0a7a4c14f 100644 --- a/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/CreateOrder.hs +++ b/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/CreateOrder.hs @@ -16,9 +16,13 @@ module Kernel.External.Payment.Juspay.Types.CreateOrder where import Data.Aeson +import qualified Data.ByteString.Lazy as BSL +import qualified Data.Text as T +import qualified Data.Text.Encoding as DT import Kernel.External.Payment.Juspay.Types.Common import Kernel.Prelude import Kernel.Types.Price +import Servant.API (FromHttpApiData (..), ToHttpApiData (..)) data CreateOrderReq = CreateOrderReq { order_id :: Text, @@ -41,10 +45,53 @@ data CreateOrderReq = CreateOrderReq mandate_end_date :: Maybe Text, metadata_gateway_reference_id :: Maybe Text, options_get_upi_deep_links :: Maybe Bool, - metadata_expiry_in_mins :: Maybe Int + metadata_expiry_in_mins :: Maybe Int, + split_settlement_details :: Maybe SplitSettlementDetails } deriving stock (Show, Eq, Generic) +data Split = Split + { amount :: HighPrecMoney, + merchant_commission :: HighPrecMoney, + sub_mid :: Text + } + deriving stock (Show, Eq, Generic) + deriving anyclass (ToJSON, FromJSON) + +newtype Vendor = Vendor + { split :: [Split] + } + deriving stock (Show, Eq, Generic) + deriving anyclass (ToJSON, FromJSON) + +data SplitSettlementDetails = SplitSettlementDetails + { marketplace :: Marketplace, + mdr_borne_by :: Text, + vendor :: Vendor + } + deriving stock (Show, Eq, Generic) + deriving anyclass (ToJSON, FromJSON) + +instance FromHttpApiData SplitSettlementDetails where + parseUrlPiece = parseHeader . DT.encodeUtf8 + parseQueryParam = parseUrlPiece + parseHeader = left T.pack . eitherDecode . BSL.fromStrict + +instance ToHttpApiData SplitSettlementDetails where + toUrlPiece = DT.decodeUtf8 . toHeader + toQueryParam = toUrlPiece + toHeader = BSL.toStrict . encode + +data MBY = MARKETPLACE | VENDOR | ALL + deriving stock (Show, Eq, Generic) + deriving anyclass (ToJSON, FromJSON) + +newtype Marketplace = Marketplace + { amount :: HighPrecMoney + } + deriving stock (Show, Eq, Generic) + deriving anyclass (ToJSON, FromJSON) + jsonReqOptions :: Options jsonReqOptions = defaultOptions @@ -58,6 +105,7 @@ jsonReqOptions = "metadata_remarks" -> "metadata.AXIS_BIZ:remarks" "metadata_gateway_reference_id" -> "metadata.JUSPAY:gateway_reference_id" "metadata_expiry_in_mins" -> "metadata.expiryInMins" + "split_settlement_details" -> "metadata.split_settlement_details" other -> other } diff --git a/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Mandate.hs b/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Mandate.hs index 87a025d1e..fb0e21750 100644 --- a/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Mandate.hs +++ b/lib/mobility-core/src/Kernel/External/Payment/Juspay/Types/Mandate.hs @@ -4,6 +4,7 @@ module Kernel.External.Payment.Juspay.Types.Mandate where import Kernel.External.Payment.Juspay.Types.Common (NotificationStatus, TransactionStatus) +import Kernel.External.Payment.Juspay.Types.CreateOrder (SplitSettlementDetails) import Kernel.Prelude import Servant (ToHttpApiData (..)) import Web.FormUrlEncoded @@ -74,7 +75,8 @@ data ProviderResponse = ProviderResponse data MandateOrder = MandateOrder { orderId :: Text, orderAmount :: Text, - orderCustomerId :: Text + orderCustomerId :: Text, + splitSettlementDetails :: Maybe SplitSettlementDetails } data MandateInfo = MandateInfo @@ -99,7 +101,8 @@ instance ToForm MandateExecutionReq where ("mandate.notification_id", toQueryParam (notificationId mandate)), ("mandate.execution_date", toQueryParam (executionDate mandate)), ("merchant_id", toQueryParam merchantId), - ("format", toQueryParam format) + ("format", toQueryParam format), + ("order.metadata.split_settlement_details", toQueryParam (splitSettlementDetails order)) ] data MandateExecutionRes = MandateExecutionRes