From 949e84e88fcb58db388bef536e89ab0510f5ede9 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 17 Jul 2024 00:17:45 +0000 Subject: [PATCH 01/26] chore(version): 2024.07.17.0 --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1b90800d03..bddc346a70ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,28 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.07.17.0 + +### Features + +- **mca:** Added recipient connector call for open banking connectors ([#3758](https://github.com/juspay/hyperswitch/pull/3758)) ([`3951ac6`](https://github.com/juspay/hyperswitch/commit/3951ac6578359c62a9b12582f5a5bbeef4c1b769)) +- **webhooks:** Add support for custom outgoing webhook http headers ([#5275](https://github.com/juspay/hyperswitch/pull/5275)) ([`101b21f`](https://github.com/juspay/hyperswitch/commit/101b21f52d69619fac1cc2f71597514f7cbee027)) + +### Bug Fixes + +- **database:** Modified_at updated for every state change for Payment Attempts ([#5312](https://github.com/juspay/hyperswitch/pull/5312)) ([`926dcd3`](https://github.com/juspay/hyperswitch/commit/926dcd3a3c0f3c09d39767e2a2c5721a42272322)) +- **logs:** Ignore request headers while logging ([#5273](https://github.com/juspay/hyperswitch/pull/5273)) ([`34d2cec`](https://github.com/juspay/hyperswitch/commit/34d2cec151d2ceeb51a638a661bc1a4c716b2859)) +- **payment_methods:** Set `requires_cvv` to false when either `connector_mandate_details` or `network_transaction_id` is present during MITs ([#5331](https://github.com/juspay/hyperswitch/pull/5331)) ([`29f8732`](https://github.com/juspay/hyperswitch/commit/29f8732d308ee3a90eba1c6ddeb8b3cedde3e3d3)) +- **routing:** Do not update `perform_session_flow_routing` output if the `SessionRoutingChoice` is none ([#5336](https://github.com/juspay/hyperswitch/pull/5336)) ([`f07689e`](https://github.com/juspay/hyperswitch/commit/f07689e75b8be8f79471b09b3752f0d5a4cec7f5)) + +### Miscellaneous Tasks + +- Create justfile for running commands for v1 and v2 migrations ([#5325](https://github.com/juspay/hyperswitch/pull/5325)) ([`23bfceb`](https://github.com/juspay/hyperswitch/commit/23bfceb6c8d3bc62d1e97f1b5feaba2dbbf9bcde)) + +**Full Changelog:** [`2024.07.16.0...2024.07.17.0`](https://github.com/juspay/hyperswitch/compare/2024.07.16.0...2024.07.17.0) + +- - - + ## 2024.07.16.0 ### Features From ecc862c3543be37e2cc7959f450ca51770978ae5 Mon Sep 17 00:00:00 2001 From: Kiran Kumar <60121719+KiranKBR@users.noreply.github.com> Date: Wed, 17 Jul 2024 12:57:46 +0530 Subject: [PATCH 02/26] refactor(connector): added amount conversion framework for checkout,adyen and globalpay (#4974) Co-authored-by: Sahkal Poddar Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Sahkal Poddar Co-authored-by: Hrithikesh <61539176+hrithikesh026@users.noreply.github.com> Co-authored-by: Narayan Bhat --- .../src/router_data.rs | 2 +- .../src/router_request_types.rs | 3 + crates/router/src/connector/adyen.rs | 102 ++++++++------ .../src/connector/adyen/transformers.rs | 49 +++---- crates/router/src/connector/adyenplatform.rs | 31 ++++- .../connector/adyenplatform/transformers.rs | 18 +++ .../adyenplatform/transformers/payouts.rs | 31 +++-- crates/router/src/connector/checkout.rs | 56 +++++--- .../src/connector/checkout/transformers.rs | 43 +++--- crates/router/src/connector/globalpay.rs | 60 +++++++-- .../src/connector/globalpay/requests.rs | 33 +++-- .../src/connector/globalpay/response.rs | 8 +- .../src/connector/globalpay/transformers.rs | 127 +++++++++++++----- crates/router/src/connector/utils.rs | 48 +++---- .../router/src/core/payments/transformers.rs | 1 + crates/router/src/types/api.rs | 12 +- crates/router/tests/connectors/adyen.rs | 4 +- crates/router/tests/connectors/bamboraapac.rs | 2 +- crates/router/tests/connectors/checkout.rs | 2 +- crates/router/tests/connectors/datatrans.rs | 2 +- crates/router/tests/connectors/globalpay.rs | 2 +- crates/router/tests/connectors/wise.rs | 2 +- 22 files changed, 415 insertions(+), 223 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs index edf465e0d28c..248c1f352981 100644 --- a/crates/hyperswitch_domain_models/src/router_data.rs +++ b/crates/hyperswitch_domain_models/src/router_data.rs @@ -148,7 +148,7 @@ pub struct RecurringMandatePaymentData { #[derive(Debug, Clone)] pub struct PaymentMethodBalance { - pub amount: i64, + pub amount: MinorUnit, pub currency: common_enums::enums::Currency, } diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index f30e6010cfff..1c2d94bae1a1 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -756,4 +756,7 @@ pub struct SetupMandateRequestData { pub payment_method_type: Option, pub request_incremental_authorization: bool, pub metadata: Option, + + // MinorUnit for amount framework + pub minor_amount: Option, } diff --git a/crates/router/src/connector/adyen.rs b/crates/router/src/connector/adyen.rs index 4fee5ea9bb0d..bb22369f2b5e 100644 --- a/crates/router/src/connector/adyen.rs +++ b/crates/router/src/connector/adyen.rs @@ -1,10 +1,11 @@ pub mod transformers; -use std::fmt::Debug; - use api_models::{enums::PaymentMethodType, webhooks::IncomingWebhookEvent}; use base64::Engine; -use common_utils::request::RequestContent; +use common_utils::{ + request::RequestContent, + types::{AmountConvertor, MinorUnit, MinorUnitForConnector}, +}; use diesel_models::{enums as storage_enums, enums}; use error_stack::{report, ResultExt}; use masking::{ExposeInterface, Secret}; @@ -18,7 +19,7 @@ use crate::connector::utils::PayoutsData; use crate::{ capture_method_not_supported, configs::settings, - connector::utils::PaymentMethodDataType, + connector::utils::{convert_amount, PaymentMethodDataType}, consts, core::errors::{self, CustomResult}, events::connector_api_logs::ConnectorEvent, @@ -39,8 +40,18 @@ use crate::{ const ADYEN_API_VERSION: &str = "v68"; -#[derive(Debug, Clone)] -pub struct Adyen; +#[derive(Clone)] +pub struct Adyen { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Adyen { + pub const fn new() -> &'static Self { + &Self { + amount_converter: &MinorUnitForConnector, + } + } +} impl ConnectorCommon for Adyen { fn id(&self) -> &'static str { @@ -377,12 +388,14 @@ impl req, types::PaymentsAuthorizeData::foreign_from(req), )); - let connector_router_data = adyen::AdyenRouterData::try_from(( - &self.get_currency_unit(), + + let amount = convert_amount( + self.amount_converter, + authorize_req.request.minor_amount, authorize_req.request.currency, - authorize_req.request.amount, - &authorize_req, - ))?; + )?; + + let connector_router_data = adyen::AdyenRouterData::try_from((amount, &authorize_req))?; let connector_req = adyen::AdyenPaymentRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) @@ -511,12 +524,13 @@ impl req: &types::PaymentsCaptureRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = adyen::AdyenRouterData::try_from(( - &self.get_currency_unit(), + let amount_to_capture = convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, req.request.currency, - req.request.amount_to_capture, - req, - ))?; + )?; + + let connector_router_data = adyen::AdyenRouterData::try_from((amount_to_capture, req))?; let connector_req = adyen::AdyenCaptureRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -795,12 +809,12 @@ impl req: &types::PaymentsAuthorizeRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = adyen::AdyenRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = adyen::AdyenRouterData::try_from((amount, req))?; let connector_req = adyen::AdyenPaymentRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -961,13 +975,15 @@ impl field_name: "currency", })?, }; - let amount = match data.request.amount { + let amount = match data.request.minor_amount { Some(amount) => amount, None => Err(errors::ConnectorError::MissingRequiredField { field_name: "amount", })?, }; + let amount = convert_amount(self.amount_converter, amount, currency)?; + if response.balance.currency != currency || response.balance.value < amount { Ok(types::RouterData { response: Err(types::ErrorResponse { @@ -1266,12 +1282,12 @@ impl services::ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = adyen::AdyenRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.destination_currency, - req.request.amount, - req, - ))?; + )?; + let connector_router_data = adyen::AdyenRouterData::try_from((amount, req))?; let connector_req = adyen::AdyenPayoutCreateRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -1372,12 +1388,13 @@ impl req: &types::PayoutsRouterData, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = adyen::AdyenRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.destination_currency, - req.request.amount, - req, - ))?; + )?; + + let connector_router_data = adyen::AdyenRouterData::try_from((amount, req))?; let connector_req = adyen::AdyenPayoutEligibilityRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -1506,12 +1523,13 @@ impl services::ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = adyen::AdyenRouterData::try_from(( - &self.get_currency_unit(), + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.destination_currency, - req.request.amount, - req, - ))?; + )?; + + let connector_router_data = adyen::AdyenRouterData::try_from((amount, req))?; let connector_req = adyen::AdyenPayoutFulfillRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -1618,12 +1636,12 @@ impl services::ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = adyen::AdyenRouterData::try_from(( - &self.get_currency_unit(), + let refund_amount = convert_amount( + self.amount_converter, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; + )?; + let connector_router_data = adyen::AdyenRouterData::try_from((refund_amount, req))?; let connector_req = adyen::AdyenRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index 6b0e251691ef..fba42558ece0 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -2,7 +2,7 @@ use api_models::payouts::PayoutMethodData; use api_models::{enums, payments, webhooks}; use cards::CardNumber; -use common_utils::{ext_traits::Encode, id_type, pii}; +use common_utils::{errors::ParsingError, ext_traits::Encode, id_type, pii, types::MinorUnit}; use error_stack::{report, ResultExt}; use masking::{ExposeInterface, PeekInterface}; use reqwest::Url; @@ -35,27 +35,13 @@ type Error = error_stack::Report; #[derive(Debug, Serialize)] pub struct AdyenRouterData { - pub amount: i64, + pub amount: MinorUnit, pub router_data: T, } -impl - TryFrom<( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - )> for AdyenRouterData -{ +impl TryFrom<(MinorUnit, T)> for AdyenRouterData { type Error = error_stack::Report; - fn try_from( - (_currency_unit, _currency, amount, item): ( - &types::api::CurrencyUnit, - types::storage::enums::Currency, - i64, - T, - ), - ) -> Result { + fn try_from((amount, item): (MinorUnit, T)) -> Result { Ok(Self { amount, router_data: item, @@ -142,11 +128,11 @@ pub struct Address { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct LineItem { - amount_excluding_tax: Option, - amount_including_tax: Option, + amount_excluding_tax: Option, + amount_including_tax: Option, description: Option, id: Option, - tax_amount: Option, + tax_amount: Option, quantity: Option, } @@ -498,7 +484,7 @@ pub enum ActionType { #[derive(Default, Debug, Clone, Serialize, Deserialize)] pub struct Amount { pub currency: storage_enums::Currency, - pub value: i64, + pub value: MinorUnit, } #[derive(Debug, Clone, Serialize)] @@ -1757,8 +1743,8 @@ fn get_line_items(item: &AdyenRouterData<&types::PaymentsAuthorizeRouterData>) - .iter() .enumerate() .map(|(i, data)| LineItem { - amount_including_tax: Some(data.amount), - amount_excluding_tax: Some(data.amount), + amount_including_tax: Some(MinorUnit::new(data.amount)), + amount_excluding_tax: Some(MinorUnit::new(data.amount)), description: Some(data.product_name.clone()), id: Some(format!("Items #{i}")), tax_amount: None, @@ -3249,8 +3235,8 @@ impl charge_id: None, }), payment_method_balance: Some(types::PaymentMethodBalance { - amount: item.response.balance.value, currency: item.response.balance.currency, + amount: item.response.balance.value, }), ..item.data }) @@ -3353,7 +3339,8 @@ pub fn get_webhook_response( }; if is_multiple_capture_psync_flow { - let capture_sync_response_list = utils::construct_captures_response_hashmap(vec![response]); + let capture_sync_response_list = + utils::construct_captures_response_hashmap(vec![response])?; Ok(( status, error, @@ -4022,7 +4009,7 @@ impl TryFrom<&AdyenRouterData<&types::RefundsRouterData>> for AdyenRefundR merchant_account: auth_type.merchant_account, amount: Amount { currency: item.router_data.request.currency, - value: item.router_data.request.refund_amount, + value: item.amount, }, merchant_refund_reason: item.router_data.request.reason.clone(), reference: item.router_data.request.refund_id.clone(), @@ -4102,7 +4089,7 @@ pub struct AdyenAdditionalDataWH { #[derive(Debug, Deserialize)] pub struct AdyenAmountWH { - pub value: i64, + pub value: MinorUnit, pub currency: storage_enums::Currency, } @@ -4385,10 +4372,8 @@ impl utils::MultipleCaptureSyncResponse for AdyenWebhookResponse { Some(self.merchant_reference_id.clone()) } - fn get_amount_captured(&self) -> Option { - self.amount - .as_ref() - .map(|amount_struct| amount_struct.value) + fn get_amount_captured(&self) -> Result, error_stack::Report> { + Ok(self.amount.clone().map(|amount| amount.value)) } } diff --git a/crates/router/src/connector/adyenplatform.rs b/crates/router/src/connector/adyenplatform.rs index e085b0bbc932..2f8cde282da6 100644 --- a/crates/router/src/connector/adyenplatform.rs +++ b/crates/router/src/connector/adyenplatform.rs @@ -1,13 +1,18 @@ pub mod transformers; -use std::fmt::Debug; #[cfg(feature = "payouts")] use common_utils::request::RequestContent; +#[cfg(feature = "payouts")] +use common_utils::types::MinorUnitForConnector; +#[cfg(feature = "payouts")] +use common_utils::types::{AmountConvertor, MinorUnit}; use error_stack::{report, ResultExt}; #[cfg(feature = "payouts")] use router_env::{instrument, tracing}; use self::transformers as adyenplatform; +#[cfg(feature = "payouts")] +use crate::connector::utils::convert_amount; use crate::{ configs::settings, core::errors::{self, CustomResult}, @@ -25,8 +30,19 @@ use crate::{ #[cfg(feature = "payouts")] use crate::{events::connector_api_logs::ConnectorEvent, utils::BytesExt}; -#[derive(Debug, Clone)] -pub struct Adyenplatform; +#[derive(Clone)] +pub struct Adyenplatform { + #[cfg(feature = "payouts")] + amount_converter: &'static (dyn AmountConvertor + Sync), +} +impl Adyenplatform { + pub const fn new() -> &'static Self { + &Self { + #[cfg(feature = "payouts")] + amount_converter: &MinorUnitForConnector, + } + } +} impl ConnectorCommon for Adyenplatform { fn id(&self) -> &'static str { @@ -200,7 +216,14 @@ impl services::ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_req = adyenplatform::AdyenTransferRequest::try_from(req)?; + let amount = convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.destination_currency, + )?; + let connector_router_data = + adyenplatform::AdyenPlatformRouterData::try_from((amount, req))?; + let connector_req = adyenplatform::AdyenTransferRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/router/src/connector/adyenplatform/transformers.rs b/crates/router/src/connector/adyenplatform/transformers.rs index 4b74de3f7ffe..20e26c427bf0 100644 --- a/crates/router/src/connector/adyenplatform/transformers.rs +++ b/crates/router/src/connector/adyenplatform/transformers.rs @@ -1,5 +1,7 @@ +use common_utils::types::MinorUnit; use error_stack::Report; use masking::Secret; +use serde::Serialize; #[cfg(feature = "payouts")] pub mod payouts; @@ -27,3 +29,19 @@ impl TryFrom<&types::ConnectorAuthType> for AdyenplatformAuthType { } } } + +#[derive(Debug, Serialize)] +pub struct AdyenPlatformRouterData { + pub amount: MinorUnit, + pub router_data: T, +} + +impl TryFrom<(MinorUnit, T)> for AdyenPlatformRouterData { + type Error = Report; + fn try_from((amount, item): (MinorUnit, T)) -> Result { + Ok(Self { + amount, + router_data: item, + }) + } +} diff --git a/crates/router/src/connector/adyenplatform/transformers/payouts.rs b/crates/router/src/connector/adyenplatform/transformers/payouts.rs index 03e685d7c787..aac51e5e8211 100644 --- a/crates/router/src/connector/adyenplatform/transformers/payouts.rs +++ b/crates/router/src/connector/adyenplatform/transformers/payouts.rs @@ -3,7 +3,7 @@ use error_stack::{report, ResultExt}; use masking::Secret; use serde::{Deserialize, Serialize}; -use super::Error; +use super::{AdyenPlatformRouterData, Error}; use crate::{ connector::{ adyen::transformers as adyen, @@ -172,11 +172,13 @@ pub enum AdyenTransactionType { Refund, } -impl TryFrom<&types::PayoutsRouterData> for AdyenTransferRequest { +impl TryFrom<&AdyenPlatformRouterData<&types::PayoutsRouterData>> for AdyenTransferRequest { type Error = Error; - fn try_from(item: &types::PayoutsRouterData) -> Result { - let request = item.request.to_owned(); - match item.get_payout_method_data()? { + fn try_from( + item: &AdyenPlatformRouterData<&types::PayoutsRouterData>, + ) -> Result { + let request = item.router_data.request.to_owned(); + match item.router_data.get_payout_method_data()? { payouts::PayoutMethodData::Card(_) | payouts::PayoutMethodData::Wallet(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Adyenplatform"), @@ -204,12 +206,17 @@ impl TryFrom<&types::PayoutsRouterData> for AdyenTransferRequest { connector: "Adyenplatform", })?, }; - let billing_address = item.get_optional_billing(); + let billing_address = item.router_data.get_optional_billing(); let address = adyen::get_address_info(billing_address).transpose()?; let account_holder = AdyenBankAccountHolder { address, - full_name: item.get_billing_full_name()?, - customer_id: Some(item.get_customer_id()?.get_string_repr().to_owned()), + full_name: item.router_data.get_billing_full_name()?, + customer_id: Some( + item.router_data + .get_customer_id()? + .get_string_repr() + .to_owned(), + ), entity_type: Some(EntityType::from(request.entity_type)), }; let counterparty = AdyenPayoutMethodDetails { @@ -220,7 +227,9 @@ impl TryFrom<&types::PayoutsRouterData> for AdyenTransferRequest { }; let adyen_connector_metadata_object = - AdyenPlatformConnectorMetadataObject::try_from(&item.connector_meta_data)?; + AdyenPlatformConnectorMetadataObject::try_from( + &item.router_data.connector_meta_data, + )?; let balance_account_id = adyen_connector_metadata_object .source_balance_account .ok_or(errors::ConnectorError::InvalidConnectorConfig { @@ -235,7 +244,7 @@ impl TryFrom<&types::PayoutsRouterData> for AdyenTransferRequest { let payout_type = request.get_payout_type()?; Ok(Self { amount: adyen::Amount { - value: request.amount, + value: item.amount, currency: request.destination_currency, }, balance_account_id, @@ -244,7 +253,7 @@ impl TryFrom<&types::PayoutsRouterData> for AdyenTransferRequest { priority: AdyenPayoutPriority::from(priority), reference: request.payout_id.clone(), reference_for_beneficiary: request.payout_id, - description: item.description.clone(), + description: item.router_data.description.clone(), }) } } diff --git a/crates/router/src/connector/checkout.rs b/crates/router/src/connector/checkout.rs index 3c628fe9ec3d..d4170d3c3b66 100644 --- a/crates/router/src/connector/checkout.rs +++ b/crates/router/src/connector/checkout.rs @@ -1,8 +1,11 @@ pub mod transformers; -use std::fmt::Debug; - -use common_utils::{crypto, ext_traits::ByteSliceExt, request::RequestContent}; +use common_utils::{ + crypto, + ext_traits::ByteSliceExt, + request::RequestContent, + types::{AmountConvertor, MinorUnit, MinorUnitForConnector}, +}; use diesel_models::enums; use error_stack::ResultExt; use masking::PeekInterface; @@ -33,8 +36,18 @@ use crate::{ utils::BytesExt, }; -#[derive(Debug, Clone)] -pub struct Checkout; +#[derive(Clone)] +pub struct Checkout { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Checkout { + pub fn new() -> &'static Self { + &Self { + amount_converter: &MinorUnitForConnector, + } + } +} impl ConnectorCommonExt for Checkout where @@ -334,12 +347,13 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = checkout::CheckoutRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, req.request.currency, - req.request.amount_to_capture, - req, - ))?; + )?; + + let connector_router_data = checkout::CheckoutRouterData::from((amount, req)); let connector_req = checkout::PaymentCaptureRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -522,12 +536,13 @@ impl ConnectorIntegration CustomResult { - let connector_router_data = checkout::CheckoutRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, req.request.currency, - req.request.amount, - req, - ))?; + )?; + + let connector_router_data = checkout::CheckoutRouterData::from((amount, req)); let connector_req = checkout::PaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -704,12 +719,13 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_router_data = checkout::CheckoutRouterData::try_from(( - &self.get_currency_unit(), + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, req.request.currency, - req.request.refund_amount, - req, - ))?; + )?; + + let connector_router_data = checkout::CheckoutRouterData::from((amount, req)); let connector_req = checkout::RefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index e035c102d209..de8a8de98a9c 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -1,4 +1,8 @@ -use common_utils::{errors::CustomResult, ext_traits::ByteSliceExt}; +use common_utils::{ + errors::{CustomResult, ParsingError}, + ext_traits::ByteSliceExt, + types::MinorUnit, +}; use error_stack::ResultExt; use masking::{ExposeInterface, Secret}; use serde::{Deserialize, Serialize}; @@ -19,19 +23,16 @@ use crate::{ #[derive(Debug, Serialize)] pub struct CheckoutRouterData { - pub amount: i64, + pub amount: MinorUnit, pub router_data: T, } -impl TryFrom<(&api::CurrencyUnit, enums::Currency, i64, T)> for CheckoutRouterData { - type Error = error_stack::Report; - fn try_from( - (_currency_unit, _currency, amount, item): (&api::CurrencyUnit, enums::Currency, i64, T), - ) -> Result { - Ok(Self { +impl From<(MinorUnit, T)> for CheckoutRouterData { + fn from((amount, item): (MinorUnit, T)) -> Self { + Self { amount, router_data: item, - }) + } } } @@ -222,7 +223,7 @@ pub struct ReturnUrl { #[derive(Debug, Serialize)] pub struct PaymentsRequest { pub source: PaymentSource, - pub amount: i64, + pub amount: MinorUnit, pub currency: String, pub processing_channel_id: Secret, #[serde(rename = "3ds")] @@ -591,7 +592,7 @@ pub struct Links { #[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)] pub struct PaymentsResponse { id: String, - amount: Option, + amount: Option, currency: Option, action_id: Option, status: CheckoutPaymentStatus, @@ -755,11 +756,11 @@ impl TryFrom> let capture_sync_response_list = match item.response { PaymentsResponseEnum::PaymentResponse(payments_response) => { // for webhook consumption flow - utils::construct_captures_response_hashmap(vec![payments_response]) + utils::construct_captures_response_hashmap(vec![payments_response])? } PaymentsResponseEnum::ActionResponse(action_list) => { // for captures sync - utils::construct_captures_response_hashmap(action_list) + utils::construct_captures_response_hashmap(action_list)? } }; Ok(Self { @@ -835,7 +836,7 @@ pub enum CaptureType { #[derive(Debug, Serialize)] pub struct PaymentCaptureRequest { - pub amount: Option, + pub amount: Option, pub capture_type: Option, pub processing_channel_id: Secret, pub reference: Option, @@ -922,7 +923,7 @@ impl TryFrom> #[derive(Clone, Debug, Serialize, Deserialize)] pub struct RefundRequest { - amount: Option, + amount: Option, reference: String, } @@ -1020,7 +1021,7 @@ pub enum ActionType { pub struct ActionResponse { #[serde(rename = "id")] pub action_id: String, - pub amount: i64, + pub amount: MinorUnit, #[serde(rename = "type")] pub action_type: ActionType, pub approved: Option, @@ -1058,8 +1059,8 @@ impl utils::MultipleCaptureSyncResponse for ActionResponse { self.action_type == ActionType::Capture } - fn get_amount_captured(&self) -> Option { - Some(self.amount) + fn get_amount_captured(&self) -> Result, error_stack::Report> { + Ok(Some(self.amount)) } } @@ -1079,8 +1080,8 @@ impl utils::MultipleCaptureSyncResponse for Box { fn is_capture_response(&self) -> bool { self.status == CheckoutPaymentStatus::Captured } - fn get_amount_captured(&self) -> Option { - self.amount.map(Into::into) + fn get_amount_captured(&self) -> Result, error_stack::Report> { + Ok(self.amount) } } @@ -1211,7 +1212,7 @@ pub struct CheckoutWebhookData { pub payment_id: Option, pub action_id: Option, pub reference: Option, - pub amount: i32, + pub amount: MinorUnit, pub balances: Option, pub response_code: Option, pub response_summary: Option, diff --git a/crates/router/src/connector/globalpay.rs b/crates/router/src/connector/globalpay.rs index d00b0e545a98..5c1a8246f07b 100644 --- a/crates/router/src/connector/globalpay.rs +++ b/crates/router/src/connector/globalpay.rs @@ -2,9 +2,8 @@ mod requests; use super::utils as connector_utils; mod response; pub mod transformers; -use std::fmt::Debug; - use ::common_utils::{errors::ReportSwitchExt, ext_traits::ByteSliceExt, request::RequestContent}; +use common_utils::types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector}; use diesel_models::enums; use error_stack::ResultExt; use masking::PeekInterface; @@ -40,8 +39,18 @@ use crate::{ utils::{crypto, BytesExt}, }; -#[derive(Debug, Clone)] -pub struct Globalpay; +#[derive(Clone)] +pub struct Globalpay { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Globalpay { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMinorUnitForConnector, + } + } +} impl ConnectorCommonExt for Globalpay where @@ -60,7 +69,9 @@ where Ok(vec![ ( headers::CONTENT_TYPE.to_string(), - self.get_content_type().to_string().into(), + types::PaymentsAuthorizeType::get_content_type(self) + .to_string() + .into(), ), ("X-GP-Version".to_string(), "2021-03-22".to_string().into()), ( @@ -442,7 +453,17 @@ impl ConnectorIntegration CustomResult { - let connector_req = requests::GlobalpayCancelRequest::try_from(req)?; + let amount = req + .request + .minor_amount + .and_then(|amount| { + req.request.currency.map(|currency| { + connector_utils::convert_amount(self.amount_converter, amount, currency) + }) + }) + .transpose()?; + let connector_router_data = requests::GlobalpayCancelRouterData::from((amount, req)); + let connector_req = requests::GlobalpayCancelRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -597,7 +618,14 @@ impl ConnectorIntegration CustomResult { - let connector_req = requests::GlobalpayCaptureRequest::try_from(req)?; + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount_to_capture, + req.request.currency, + )?; + + let connector_router_data = requests::GlobalPayRouterData::from((amount, req)); + let connector_req = requests::GlobalpayCaptureRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -687,7 +715,14 @@ impl ConnectorIntegration CustomResult { - let connector_req = GlobalpayPaymentsRequest::try_from(req)?; + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + + let connector_router_data = requests::GlobalPayRouterData::from((amount, req)); + let connector_req = GlobalpayPaymentsRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } @@ -778,7 +813,14 @@ impl ConnectorIntegration, _connectors: &settings::Connectors, ) -> CustomResult { - let connector_req = requests::GlobalpayRefundRequest::try_from(req)?; + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, + req.request.currency, + )?; + + let connector_router_data = requests::GlobalPayRouterData::from((amount, req)); + let connector_req = requests::GlobalpayRefundRequest::try_from(&connector_router_data)?; Ok(RequestContent::Json(Box::new(connector_req))) } diff --git a/crates/router/src/connector/globalpay/requests.rs b/crates/router/src/connector/globalpay/requests.rs index 79785563f3c1..9ccfcd90be38 100644 --- a/crates/router/src/connector/globalpay/requests.rs +++ b/crates/router/src/connector/globalpay/requests.rs @@ -1,13 +1,26 @@ +use common_utils::types::StringMinorUnit; use masking::Secret; use serde::{Deserialize, Serialize}; +#[derive(Debug, Serialize)] +pub struct GlobalPayRouterData { + pub amount: StringMinorUnit, + pub router_data: T, +} + +#[derive(Debug, Serialize)] +pub struct GlobalpayCancelRouterData { + pub amount: Option, + pub router_data: T, +} + #[derive(Debug, Serialize)] pub struct GlobalpayPaymentsRequest { /// A meaningful label for the merchant account set by Global Payments. pub account_name: Secret, /// The amount to transfer between Payer and Merchant for a SALE or a REFUND. It is always /// represented in the lowest denomiation of the related currency. - pub amount: Option, + pub amount: Option, /// Indicates if the merchant would accept an authorization for an amount less than the /// requested amount. This is available for CP channel /// only where the balance not authorized can be processed again using a different card. @@ -17,14 +30,14 @@ pub struct GlobalpayPaymentsRequest { pub capture_mode: Option, /// The amount of the transaction that relates to cashback.It is always represented in the /// lowest denomiation of the related currency. - pub cashback_amount: Option, + pub cashback_amount: Option, /// Describes whether the transaction was processed in a face to face(CP) scenario or a /// Customer Not Present (CNP) scenario. pub channel: Channel, /// The amount that reflects the charge the merchant applied to the transaction for availing /// of a more convenient purchase.It is always represented in the lowest denomiation of the /// related currency. - pub convenience_amount: Option, + pub convenience_amount: Option, /// The country in ISO-3166-1(alpha-2 code) format. pub country: api_models::enums::CountryAlpha2, /// The currency of the amount in ISO-4217(alpha-3) @@ -37,7 +50,7 @@ pub struct GlobalpayPaymentsRequest { pub device: Option, /// The amount of the gratuity for a transaction.It is always represented in the lowest /// denomiation of the related currency. - pub gratuity_amount: Option, + pub gratuity_amount: Option, /// Indicates whether the Merchant or the Payer initiated the creation of a transaction. pub initiator: Option, /// Indicates the source IP Address of the system used to create the transaction. @@ -64,7 +77,7 @@ pub struct GlobalpayPaymentsRequest { /// The amount that reflects the additional charge the merchant applied to the transaction /// for using a specific payment method.It is always represented in the lowest denomiation of /// the related currency. - pub surcharge_amount: Option, + pub surcharge_amount: Option, /// Indicates the total or expected total of captures that will executed against a /// transaction flagged as being captured multiple times. pub total_capture_count: Option, @@ -138,7 +151,7 @@ pub struct Lodging { /// A reference that identifies the booking reference for a lodging stay. pub booking_reference: Option, /// The amount charged for one nights lodging. - pub daily_rate_amount: Option, + pub daily_rate_amount: Option, /// A reference that identifies the booking reference for a lodging stay. pub date_checked_in: Option, /// The check out date for a lodging stay. @@ -156,7 +169,7 @@ pub struct LodgingChargeItem { /// A reference that identifies the charge item, such as a lodging folio number. pub reference: Option, /// The total amount for the list of charge types for a charge item. - pub total_amount: Option, + pub total_amount: Option, pub types: Option>, } @@ -799,17 +812,17 @@ pub enum Sequence { #[derive(Default, Debug, Serialize)] pub struct GlobalpayRefundRequest { - pub amount: String, + pub amount: StringMinorUnit, } #[derive(Default, Debug, Serialize)] pub struct GlobalpayCaptureRequest { - pub amount: Option, + pub amount: Option, pub capture_sequence: Option, pub reference: Option, } #[derive(Default, Debug, Serialize)] pub struct GlobalpayCancelRequest { - pub amount: Option, + pub amount: Option, } diff --git a/crates/router/src/connector/globalpay/response.rs b/crates/router/src/connector/globalpay/response.rs index 95f8c33728e0..228562a1bc24 100644 --- a/crates/router/src/connector/globalpay/response.rs +++ b/crates/router/src/connector/globalpay/response.rs @@ -1,3 +1,5 @@ +use common_enums::Currency; +use common_utils::types::StringMinorUnit; use masking::Secret; use serde::{Deserialize, Serialize}; @@ -13,7 +15,7 @@ pub struct GlobalpayPaymentsResponse { pub action: Option, /// The amount to transfer between Payer and Merchant for a SALE or a REFUND. It is always /// represented in the lowest denomiation of the related currency. - pub amount: Option, + pub amount: Option, /// Indicates if the merchant would accept an authorization for an amount less than the /// requested amount. This is available for CP channel /// only where the balance not authorized can be processed again using a different card. @@ -29,7 +31,7 @@ pub struct GlobalpayPaymentsResponse { /// The country in ISO-3166-1(alpha-2 code) format. pub country: Option, /// The currency of the amount in ISO-4217(alpha-3) - pub currency: Option, + pub currency: Option, /// Information relating to a currency conversion. pub currency_conversion: Option, /// A unique identifier generated by Global Payments to identify the transaction. @@ -96,7 +98,7 @@ pub struct CurrencyConversion { /// The exchange rate used to convert one currency to another. pub margin_rate_percentage: Option, /// The amount that will affect the payer's account. - pub payer_amount: Option, + pub payer_amount: Option, /// The currency of the amount that will affect the payer's account. pub payer_currency: Option, } diff --git a/crates/router/src/connector/globalpay/transformers.rs b/crates/router/src/connector/globalpay/transformers.rs index 3bdc3360a797..efc6661b447b 100644 --- a/crates/router/src/connector/globalpay/transformers.rs +++ b/crates/router/src/connector/globalpay/transformers.rs @@ -1,4 +1,7 @@ -use common_utils::crypto::{self, GenerateDigest}; +use common_utils::{ + crypto::{self, GenerateDigest}, + types::{AmountConvertor, MinorUnit, StringMinorUnit, StringMinorUnitForConnector}, +}; use error_stack::ResultExt; use masking::{ExposeInterface, PeekInterface, Secret}; use rand::distributions::DistString; @@ -7,8 +10,9 @@ use url::Url; use super::{ requests::{ - self, ApmProvider, GlobalpayPaymentsRequest, GlobalpayRefreshTokenRequest, Initiator, - PaymentMethodData, Sequence, StoredCredential, + self, ApmProvider, GlobalPayRouterData, GlobalpayCancelRouterData, + GlobalpayPaymentsRequest, GlobalpayRefreshTokenRequest, Initiator, PaymentMethodData, + Sequence, StoredCredential, }, response::{GlobalpayPaymentStatus, GlobalpayPaymentsResponse, GlobalpayRefreshTokenResponse}, }; @@ -20,6 +24,24 @@ use crate::{ types::{self, api, domain, storage::enums, transformers::ForeignTryFrom, ErrorResponse}, }; +impl From<(StringMinorUnit, T)> for GlobalPayRouterData { + fn from((amount, item): (StringMinorUnit, T)) -> Self { + Self { + amount, + router_data: item, + } + } +} + +impl From<(Option, T)> for GlobalpayCancelRouterData { + fn from((amount, item): (Option, T)) -> Self { + Self { + amount, + router_data: item, + } + } +} + type Error = error_stack::Report; #[derive(Debug, Serialize, Deserialize)] @@ -27,21 +49,29 @@ pub struct GlobalPayMeta { account_name: Secret, } -impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobalpayPaymentsRequest { +impl TryFrom<&GlobalPayRouterData<&types::PaymentsAuthorizeRouterData>> + for GlobalpayPaymentsRequest +{ type Error = Error; - fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { + fn try_from( + item: &GlobalPayRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { let metadata: GlobalPayMeta = - utils::to_connector_meta_from_secret(item.connector_meta_data.clone())?; + utils::to_connector_meta_from_secret(item.router_data.connector_meta_data.clone())?; let account_name = metadata.account_name; - let (initiator, stored_credential, brand_reference) = get_mandate_details(item)?; - let payment_method_data = get_payment_method_data(item, brand_reference)?; + let (initiator, stored_credential, brand_reference) = + get_mandate_details(item.router_data)?; + let payment_method_data = get_payment_method_data(item.router_data, brand_reference)?; Ok(Self { account_name, - amount: Some(item.request.amount.to_string()), - currency: item.request.currency.to_string(), - reference: item.connector_request_reference_id.to_string(), - country: item.get_billing_country()?, - capture_mode: Some(requests::CaptureMode::from(item.request.capture_method)), + amount: Some(item.amount.to_owned()), + currency: item.router_data.request.currency.to_string(), + + reference: item.router_data.connector_request_reference_id.to_string(), + country: item.router_data.get_billing_country()?, + capture_mode: Some(requests::CaptureMode::from( + item.router_data.request.capture_method, + )), payment_method: requests::PaymentMethod { payment_method_data, authentication: None, @@ -56,10 +86,10 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobalpayPaymentsRequest { storage_mode: None, }, notifications: Some(requests::Notifications { - return_url: get_return_url(item), + return_url: get_return_url(item.router_data), challenge_return_url: None, decoupled_challenge_return_url: None, - status_url: item.request.webhook_url.clone(), + status_url: item.router_data.request.webhook_url.clone(), three_ds_method_return_url: None, }), authorization_mode: None, @@ -86,19 +116,29 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobalpayPaymentsRequest { } } -impl TryFrom<&types::PaymentsCaptureRouterData> for requests::GlobalpayCaptureRequest { +impl TryFrom<&GlobalPayRouterData<&types::PaymentsCaptureRouterData>> + for requests::GlobalpayCaptureRequest +{ type Error = Error; - fn try_from(value: &types::PaymentsCaptureRouterData) -> Result { + fn try_from( + value: &GlobalPayRouterData<&types::PaymentsCaptureRouterData>, + ) -> Result { Ok(Self { - amount: Some(value.request.amount_to_capture.to_string()), - capture_sequence: value.request.multiple_capture_data.clone().map(|mcd| { - if mcd.capture_sequence == 1 { - Sequence::First - } else { - Sequence::Subsequent - } - }), + amount: Some(value.amount.to_owned()), + capture_sequence: value + .router_data + .request + .multiple_capture_data + .clone() + .map(|mcd| { + if mcd.capture_sequence == 1 { + Sequence::First + } else { + Sequence::Subsequent + } + }), reference: value + .router_data .request .multiple_capture_data .as_ref() @@ -107,11 +147,15 @@ impl TryFrom<&types::PaymentsCaptureRouterData> for requests::GlobalpayCaptureRe } } -impl TryFrom<&types::PaymentsCancelRouterData> for requests::GlobalpayCancelRequest { +impl TryFrom<&GlobalpayCancelRouterData<&types::PaymentsCancelRouterData>> + for requests::GlobalpayCancelRequest +{ type Error = Error; - fn try_from(value: &types::PaymentsCancelRouterData) -> Result { + fn try_from( + value: &GlobalpayCancelRouterData<&types::PaymentsCancelRouterData>, + ) -> Result { Ok(Self { - amount: value.request.amount.map(|amount| amount.to_string()), + amount: value.amount.clone(), }) } } @@ -295,7 +339,7 @@ impl ) -> Result { if is_multiple_capture_sync { let capture_sync_response_list = - utils::construct_captures_response_hashmap(vec![value.response]); + utils::construct_captures_response_hashmap(vec![value.response])?; Ok(Self { response: Ok(types::PaymentsResponseData::MultipleCaptureResponse { capture_sync_response_list, @@ -326,11 +370,15 @@ impl } } -impl TryFrom<&types::RefundsRouterData> for requests::GlobalpayRefundRequest { +impl TryFrom<&GlobalPayRouterData<&types::RefundsRouterData>> + for requests::GlobalpayRefundRequest +{ type Error = Error; - fn try_from(item: &types::RefundsRouterData) -> Result { + fn try_from( + item: &GlobalPayRouterData<&types::RefundsRouterData>, + ) -> Result { Ok(Self { - amount: item.request.refund_amount.to_string(), + amount: item.amount.to_owned(), }) } } @@ -497,10 +545,19 @@ impl utils::MultipleCaptureSyncResponse for GlobalpayPaymentsResponse { true } - fn get_amount_captured(&self) -> Option { + fn get_amount_captured( + &self, + ) -> Result, error_stack::Report> { match self.amount.clone() { - Some(amount) => amount.parse().ok(), - None => None, + Some(amount) => { + let minor_amount = StringMinorUnitForConnector::convert_back( + &StringMinorUnitForConnector, + amount, + self.currency.unwrap_or_default(), //it is ignored in convert_back function + )?; + Ok(Some(minor_amount)) + } + None => Ok(None), } } fn get_connector_reference_id(&self) -> Option { diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index d56de472eb30..6009fe51d59d 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -12,7 +12,7 @@ use api_models::{ use base64::Engine; use common_utils::{ date_time, - errors::ReportSwitchExt, + errors::{ParsingError, ReportSwitchExt}, ext_traits::StringExt, id_type, pii::{self, Email, IpAddress}, @@ -2181,36 +2181,38 @@ pub trait MultipleCaptureSyncResponse { fn get_connector_reference_id(&self) -> Option { None } - fn get_amount_captured(&self) -> Option; + fn get_amount_captured(&self) -> Result, error_stack::Report>; } pub fn construct_captures_response_hashmap( capture_sync_response_list: Vec, -) -> HashMap +) -> CustomResult, errors::ConnectorError> where T: MultipleCaptureSyncResponse, { let mut hashmap = HashMap::new(); - capture_sync_response_list - .into_iter() - .for_each(|capture_sync_response| { - let connector_capture_id = capture_sync_response.get_connector_capture_id(); - if capture_sync_response.is_capture_response() { - hashmap.insert( - connector_capture_id.clone(), - types::CaptureSyncResponse::Success { - resource_id: ResponseId::ConnectorTransactionId(connector_capture_id), - status: capture_sync_response.get_capture_attempt_status(), - connector_response_reference_id: capture_sync_response - .get_connector_reference_id(), - amount: capture_sync_response - .get_amount_captured() - .map(MinorUnit::new), - }, - ); - } - }); - hashmap + for capture_sync_response in capture_sync_response_list { + let connector_capture_id = capture_sync_response.get_connector_capture_id(); + if capture_sync_response.is_capture_response() { + hashmap.insert( + connector_capture_id.clone(), + types::CaptureSyncResponse::Success { + resource_id: ResponseId::ConnectorTransactionId(connector_capture_id), + status: capture_sync_response.get_capture_attempt_status(), + connector_response_reference_id: capture_sync_response + .get_connector_reference_id(), + amount: capture_sync_response + .get_amount_captured() + .change_context(errors::ConnectorError::AmountConversionFailed) + .attach_printable( + "failed to convert back captured response amount to minor unit", + )?, + }, + ); + } + } + + Ok(hashmap) } pub fn is_manual_capture(capture_method: Option) -> bool { diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index a5648af427c2..9b21fddf4f04 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -1668,6 +1668,7 @@ impl TryFrom> for types::SetupMandateRequ currency: payment_data.currency, confirm: true, amount: Some(amount.get_amount_as_i64()), //need to change once we move to connector module + minor_amount: Some(amount), payment_method_data: From::from( payment_data .payment_method_data diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 5cd4efcf9b27..92da4ff1960f 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -345,10 +345,12 @@ impl ConnectorData { match enums::Connector::from_str(connector_name) { Ok(name) => match name { enums::Connector::Aci => Ok(ConnectorEnum::Old(Box::new(&connector::Aci))), - enums::Connector::Adyen => Ok(ConnectorEnum::Old(Box::new(&connector::Adyen))), - enums::Connector::Adyenplatform => { - Ok(ConnectorEnum::Old(Box::new(&connector::Adyenplatform))) + enums::Connector::Adyen => { + Ok(ConnectorEnum::Old(Box::new(connector::Adyen::new()))) } + enums::Connector::Adyenplatform => Ok(ConnectorEnum::Old(Box::new( + connector::Adyenplatform::new(), + ))), enums::Connector::Airwallex => { Ok(ConnectorEnum::Old(Box::new(&connector::Airwallex))) } @@ -377,7 +379,7 @@ impl ConnectorData { Ok(ConnectorEnum::Old(Box::new(connector::Cashtocode::new()))) } enums::Connector::Checkout => { - Ok(ConnectorEnum::Old(Box::new(&connector::Checkout))) + Ok(ConnectorEnum::Old(Box::new(connector::Checkout::new()))) } enums::Connector::Coinbase => { Ok(ConnectorEnum::Old(Box::new(&connector::Coinbase))) @@ -424,7 +426,7 @@ impl ConnectorData { enums::Connector::Fiserv => Ok(ConnectorEnum::Old(Box::new(&connector::Fiserv))), enums::Connector::Forte => Ok(ConnectorEnum::Old(Box::new(&connector::Forte))), enums::Connector::Globalpay => { - Ok(ConnectorEnum::Old(Box::new(&connector::Globalpay))) + Ok(ConnectorEnum::Old(Box::new(connector::Globalpay::new()))) } enums::Connector::Globepay => { Ok(ConnectorEnum::Old(Box::new(&connector::Globepay))) diff --git a/crates/router/tests/connectors/adyen.rs b/crates/router/tests/connectors/adyen.rs index 06b878284a08..2e8d4ab6862a 100644 --- a/crates/router/tests/connectors/adyen.rs +++ b/crates/router/tests/connectors/adyen.rs @@ -16,7 +16,7 @@ impl utils::Connector for AdyenTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Adyen; utils::construct_connector_data_old( - Box::new(&Adyen), + Box::new(Adyen::new()), types::Connector::Adyen, types::api::GetToken::Connector, None, @@ -27,7 +27,7 @@ impl utils::Connector for AdyenTest { fn get_payout_data(&self) -> Option { use router::connector::Adyen; Some(utils::construct_connector_data_old( - Box::new(&Adyen), + Box::new(Adyen::new()), types::Connector::Adyen, types::api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/bamboraapac.rs b/crates/router/tests/connectors/bamboraapac.rs index d631dd10f1c2..3cf70b6ff523 100644 --- a/crates/router/tests/connectors/bamboraapac.rs +++ b/crates/router/tests/connectors/bamboraapac.rs @@ -11,7 +11,7 @@ impl utils::Connector for BamboraapacTest { fn get_data(&self) -> api::ConnectorData { use router::connector::Adyen; utils::construct_connector_data_old( - Box::new(&Adyen), + Box::new(Adyen::new()), types::Connector::Adyen, api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/checkout.rs b/crates/router/tests/connectors/checkout.rs index 42072158a46f..21a904a2ca1f 100644 --- a/crates/router/tests/connectors/checkout.rs +++ b/crates/router/tests/connectors/checkout.rs @@ -12,7 +12,7 @@ impl utils::Connector for CheckoutTest { fn get_data(&self) -> types::api::ConnectorData { use router::connector::Checkout; utils::construct_connector_data_old( - Box::new(&Checkout), + Box::new(Checkout::new()), types::Connector::Checkout, types::api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/datatrans.rs b/crates/router/tests/connectors/datatrans.rs index 1286311bc9af..7c040c607f90 100644 --- a/crates/router/tests/connectors/datatrans.rs +++ b/crates/router/tests/connectors/datatrans.rs @@ -11,7 +11,7 @@ impl utils::Connector for DatatransTest { fn get_data(&self) -> api::ConnectorData { use router::connector::Adyen; utils::construct_connector_data_old( - Box::new(&Adyen), + Box::new(Adyen::new()), types::Connector::Adyen, api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/globalpay.rs b/crates/router/tests/connectors/globalpay.rs index 0bbda5d5c5d8..8879631d1e3d 100644 --- a/crates/router/tests/connectors/globalpay.rs +++ b/crates/router/tests/connectors/globalpay.rs @@ -16,7 +16,7 @@ impl Connector for Globalpay { fn get_data(&self) -> api::ConnectorData { use router::connector::Globalpay; utils::construct_connector_data_old( - Box::new(&Globalpay), + Box::new(Globalpay::new()), types::Connector::Globalpay, api::GetToken::Connector, None, diff --git a/crates/router/tests/connectors/wise.rs b/crates/router/tests/connectors/wise.rs index b02af6f7fd68..761678046701 100644 --- a/crates/router/tests/connectors/wise.rs +++ b/crates/router/tests/connectors/wise.rs @@ -18,7 +18,7 @@ impl utils::Connector for WiseTest { fn get_data(&self) -> api::ConnectorData { use router::connector::Adyen; utils::construct_connector_data_old( - Box::new(&Adyen), + Box::new(Adyen::new()), types::Connector::Adyen, api::GetToken::Connector, None, From eb6f27d64e07f3f65b4e9a2f227898a238355477 Mon Sep 17 00:00:00 2001 From: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:57:50 +0530 Subject: [PATCH 03/26] feat(core): Payments core modification for open banking connectors (#3947) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- api-reference/openapi_spec.json | 77 ++++++- crates/api_models/src/payments.rs | 32 +++ crates/common_enums/src/enums.rs | 3 + crates/common_enums/src/transformers.rs | 1 + .../src/response_modifier.rs | 13 ++ crates/connector_configs/src/transformer.rs | 1 + crates/euclid/src/dssa/graph.rs | 1 + crates/euclid/src/frontend/ast/lowering.rs | 2 + crates/euclid/src/frontend/dir.rs | 17 ++ crates/euclid/src/frontend/dir/enums.rs | 21 ++ crates/euclid/src/frontend/dir/lowering.rs | 9 + .../euclid/src/frontend/dir/transformers.rs | 6 + crates/euclid_wasm/src/lib.rs | 1 + .../src/payment_method_data.rs | 19 ++ .../src/router_flow_types/payments.rs | 3 + .../src/router_request_types.rs | 31 ++- .../src/router_response_types.rs | 3 + crates/kgraph_utils/src/mca.rs | 5 + crates/kgraph_utils/src/transformers.rs | 7 + crates/openapi/src/openapi.rs | 2 + .../stripe/payment_intents/types.rs | 1 + .../stripe/setup_intents/types.rs | 1 + .../router/src/connector/aci/transformers.rs | 1 + crates/router/src/connector/adyen.rs | 3 +- .../src/connector/adyen/transformers.rs | 2 + .../src/connector/airwallex/transformers.rs | 1 + .../connector/authorizedotnet/transformers.rs | 3 + .../src/connector/bambora/transformers.rs | 1 + .../connector/bankofamerica/transformers.rs | 5 + .../src/connector/billwerk/transformers.rs | 1 + .../src/connector/bluesnap/transformers.rs | 2 + .../router/src/connector/boku/transformers.rs | 1 + .../braintree_graphql_transformers.rs | 4 + .../src/connector/braintree/transformers.rs | 1 + .../src/connector/checkout/transformers.rs | 2 + .../src/connector/cryptopay/transformers.rs | 1 + .../src/connector/cybersource/transformers.rs | 5 + .../src/connector/datatrans/transformers.rs | 1 + .../src/connector/dlocal/transformers.rs | 1 + .../src/connector/fiserv/transformers.rs | 1 + .../src/connector/forte/transformers.rs | 1 + .../src/connector/globepay/transformers.rs | 1 + .../src/connector/gocardless/transformers.rs | 2 + .../src/connector/helcim/transformers.rs | 2 + .../src/connector/iatapay/transformers.rs | 3 +- crates/router/src/connector/klarna.rs | 4 +- .../src/connector/mifinity/transformers.rs | 1 + .../connector/multisafepay/transformers.rs | 4 +- .../src/connector/nexinets/transformers.rs | 1 + .../router/src/connector/nmi/transformers.rs | 1 + .../router/src/connector/noon/transformers.rs | 1 + .../src/connector/nuvei/transformers.rs | 2 + .../src/connector/opayo/transformers.rs | 1 + .../src/connector/payeezy/transformers.rs | 2 + .../src/connector/payme/transformers.rs | 4 + .../src/connector/paypal/transformers.rs | 1 + .../src/connector/placetopay/transformers.rs | 1 + .../src/connector/powertranz/transformers.rs | 1 + .../src/connector/razorpay/transformers.rs | 1 + .../src/connector/shift4/transformers.rs | 2 + .../src/connector/square/transformers.rs | 2 + .../router/src/connector/stax/transformers.rs | 2 + .../src/connector/stripe/transformers.rs | 5 + .../src/connector/trustpay/transformers.rs | 1 + .../router/src/connector/tsys/transformers.rs | 1 + crates/router/src/connector/utils.rs | 4 + .../router/src/connector/volt/transformers.rs | 1 + .../src/connector/worldline/transformers.rs | 1 + .../src/connector/worldpay/transformers.rs | 1 + .../router/src/connector/zen/transformers.rs | 1 + .../router/src/connector/zsl/transformers.rs | 3 +- crates/router/src/core/fraud_check.rs | 1 + .../core/fraud_check/flows/checkout_flow.rs | 19 +- .../core/fraud_check/flows/record_return.rs | 16 +- .../src/core/fraud_check/flows/sale_flow.rs | 16 +- .../fraud_check/flows/transaction_flow.rs | 16 +- crates/router/src/core/payment_methods.rs | 1 + crates/router/src/core/payments.rs | 204 ++++++++++++++++-- .../connector_integration_v2_impls.rs | 19 ++ crates/router/src/core/payments/flows.rs | 119 ++++++++++ .../src/core/payments/flows/approve_flow.rs | 13 ++ .../src/core/payments/flows/authorize_flow.rs | 91 ++++++++ .../src/core/payments/flows/cancel_flow.rs | 13 ++ .../src/core/payments/flows/capture_flow.rs | 13 ++ .../payments/flows/complete_authorize_flow.rs | 13 ++ .../flows/incremental_authorization_flow.rs | 13 ++ .../src/core/payments/flows/psync_flow.rs | 13 ++ .../src/core/payments/flows/reject_flow.rs | 13 ++ .../src/core/payments/flows/session_flow.rs | 13 ++ .../core/payments/flows/setup_mandate_flow.rs | 13 ++ crates/router/src/core/payments/helpers.rs | 61 +++++- .../payments/operations/payment_response.rs | 4 +- crates/router/src/core/payments/retry.rs | 6 +- .../router/src/core/payments/transformers.rs | 51 ++++- crates/router/src/core/payout_link.rs | 1 + crates/router/src/types.rs | 14 +- crates/router/src/types/api/payments.rs | 22 +- crates/router/src/types/api/payments_v2.rs | 13 +- crates/router/src/types/transformers.rs | 2 + crates/router/tests/connectors/cashtocode.rs | 4 +- crates/router/tests/connectors/utils.rs | 2 + 101 files changed, 1101 insertions(+), 48 deletions(-) diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index e916e6ee4667..79605b77fa4b 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -12714,6 +12714,33 @@ }, "additionalProperties": false }, + "OpenBankingData": { + "oneOf": [ + { + "type": "object", + "required": [ + "open_banking_pis" + ], + "properties": { + "open_banking_pis": { + "type": "object" + } + } + } + ] + }, + "OpenBankingSessionToken": { + "type": "object", + "required": [ + "open_banking_session_token" + ], + "properties": { + "open_banking_session_token": { + "type": "string", + "description": "The session token for OpenBanking Connectors" + } + } + }, "OrderDetails": { "type": "object", "required": [ @@ -13737,7 +13764,8 @@ "real_time_payment", "upi", "voucher", - "gift_card" + "gift_card", + "open_banking" ] }, "PaymentMethodCollectLinkRequest": { @@ -14132,6 +14160,18 @@ "$ref": "#/components/schemas/CardToken" } } + }, + { + "type": "object", + "title": "OpenBanking", + "required": [ + "open_banking" + ], + "properties": { + "open_banking": { + "$ref": "#/components/schemas/OpenBankingData" + } + } } ] }, @@ -14338,6 +14378,17 @@ "type": "object" } } + }, + { + "type": "object", + "required": [ + "open_banking" + ], + "properties": { + "open_banking": { + "type": "object" + } + } } ] }, @@ -14700,7 +14751,8 @@ "seicomart", "pay_easy", "local_bank_transfer", - "mifinity" + "mifinity", + "open_banking_pis" ] }, "PaymentMethodUpdate": { @@ -20171,6 +20223,27 @@ } ] }, + { + "allOf": [ + { + "$ref": "#/components/schemas/OpenBankingSessionToken" + }, + { + "type": "object", + "required": [ + "wallet_name" + ], + "properties": { + "wallet_name": { + "type": "string", + "enum": [ + "open_banking" + ] + } + } + } + ] + }, { "type": "object", "required": [ diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index bcc67100e1c4..f53507fc13a1 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -1600,6 +1600,7 @@ mod payment_method_data_serde { | PaymentMethodData::Voucher(_) | PaymentMethodData::Card(_) | PaymentMethodData::MandatePayment + | PaymentMethodData::OpenBanking(_) | PaymentMethodData::Wallet(_) => { payment_method_data_request.serialize(serializer) } @@ -1657,6 +1658,8 @@ pub enum PaymentMethodData { GiftCard(Box), #[schema(title = "CardToken")] CardToken(CardToken), + #[schema(title = "OpenBanking")] + OpenBanking(OpenBankingData), } pub trait GetAddressFromPaymentMethodData { @@ -1680,6 +1683,7 @@ impl GetAddressFromPaymentMethodData for PaymentMethodData { | Self::Upi(_) | Self::GiftCard(_) | Self::CardToken(_) + | Self::OpenBanking(_) | Self::MandatePayment => None, } } @@ -1717,6 +1721,7 @@ impl PaymentMethodData { Self::Upi(_) => Some(api_enums::PaymentMethod::Upi), Self::Voucher(_) => Some(api_enums::PaymentMethod::Voucher), Self::GiftCard(_) => Some(api_enums::PaymentMethod::GiftCard), + Self::OpenBanking(_) => Some(api_enums::PaymentMethod::OpenBanking), Self::CardToken(_) | Self::MandatePayment => None, } } @@ -1785,6 +1790,14 @@ impl GetPaymentMethodType for PayLaterData { } } +impl GetPaymentMethodType for OpenBankingData { + fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { + match self { + Self::OpenBankingPIS {} => api_enums::PaymentMethodType::OpenBankingPIS, + } + } +} + impl GetPaymentMethodType for BankRedirectData { fn get_payment_method_type(&self) -> api_enums::PaymentMethodType { match self { @@ -1983,6 +1996,7 @@ pub enum AdditionalPaymentData { Voucher {}, CardRedirect {}, CardToken {}, + OpenBanking {}, } #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)] @@ -2673,6 +2687,12 @@ pub struct SamsungPayWalletData { pub token: Secret, } +#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub enum OpenBankingData { + #[serde(rename = "open_banking_pis")] + OpenBankingPIS {}, +} #[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)] #[serde(rename_all = "snake_case")] pub struct GooglePayWalletData { @@ -2946,6 +2966,7 @@ where | PaymentMethodDataResponse::Upi {} | PaymentMethodDataResponse::Wallet {} | PaymentMethodDataResponse::BankTransfer {} + | PaymentMethodDataResponse::OpenBanking {} | PaymentMethodDataResponse::Voucher {} => { payment_method_data_response.serialize(serializer) } @@ -2979,6 +3000,7 @@ pub enum PaymentMethodDataResponse { GiftCard {}, CardRedirect {}, CardToken {}, + OpenBanking {}, } #[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize, ToSchema)] @@ -4146,6 +4168,7 @@ impl From for PaymentMethodDataResponse { AdditionalPaymentData::GiftCard {} => Self::GiftCard {}, AdditionalPaymentData::CardRedirect {} => Self::CardRedirect {}, AdditionalPaymentData::CardToken {} => Self::CardToken {}, + AdditionalPaymentData::OpenBanking {} => Self::OpenBanking {}, } } } @@ -4531,6 +4554,8 @@ pub enum SessionToken { Paypal(Box), /// The session response structure for Apple Pay ApplePay(Box), + /// Session token for OpenBanking PIS flow + OpenBanking(OpenBankingSessionToken), /// Whenever there is no session token response or an error in session response NoSessionTokenReceived, } @@ -4607,6 +4632,13 @@ pub struct PaypalSessionTokenResponse { pub sdk_next_action: SdkNextAction, } +#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] +#[serde(rename_all = "lowercase")] +pub struct OpenBankingSessionToken { + /// The session token for OpenBanking Connectors + pub open_banking_session_token: String, +} + #[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, ToSchema)] #[serde(rename_all = "lowercase")] pub struct ApplepaySessionTokenResponse { diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 129e83ce2b6f..12281350af21 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -1569,6 +1569,8 @@ pub enum PaymentMethodType { PayEasy, LocalBankTransfer, Mifinity, + #[serde(rename = "open_banking_pis")] + OpenBankingPIS, } /// Indicates the type of payment method. Eg: 'card', 'wallet', etc. @@ -1606,6 +1608,7 @@ pub enum PaymentMethod { Upi, Voucher, GiftCard, + OpenBanking, } /// The type of the payment that differentiates between normal and various types of mandate payments. Use 'setup_mandate' in case of zero auth flow. diff --git a/crates/common_enums/src/transformers.rs b/crates/common_enums/src/transformers.rs index fc39cd1cf501..fab570542c8c 100644 --- a/crates/common_enums/src/transformers.rs +++ b/crates/common_enums/src/transformers.rs @@ -1882,6 +1882,7 @@ impl From for PaymentMethod { PaymentMethodType::FamilyMart => Self::Voucher, PaymentMethodType::Seicomart => Self::Voucher, PaymentMethodType::PayEasy => Self::Voucher, + PaymentMethodType::OpenBankingPIS => Self::OpenBanking, } } } diff --git a/crates/connector_configs/src/response_modifier.rs b/crates/connector_configs/src/response_modifier.rs index 38dc4130cf03..06f9d25b65d2 100644 --- a/crates/connector_configs/src/response_modifier.rs +++ b/crates/connector_configs/src/response_modifier.rs @@ -19,6 +19,7 @@ impl ConnectorApiIntegrationPayload { let mut voucher_details: Vec = Vec::new(); let mut gift_card_details: Vec = Vec::new(); let mut card_redirect_details: Vec = Vec::new(); + let mut open_banking_details: Vec = Vec::new(); if let Some(payment_methods_enabled) = response.payment_methods_enabled.clone() { for methods in payment_methods_enabled { @@ -160,6 +161,18 @@ impl ConnectorApiIntegrationPayload { } } } + api_models::enums::PaymentMethod::OpenBanking => { + if let Some(payment_method_types) = methods.payment_method_types { + for method_type in payment_method_types { + open_banking_details.push(Provider { + payment_method_type: method_type.payment_method_type, + accepted_currencies: method_type.accepted_currencies.clone(), + accepted_countries: method_type.accepted_countries.clone(), + payment_experience: method_type.payment_experience, + }) + } + } + } api_models::enums::PaymentMethod::Upi => { if let Some(payment_method_types) = methods.payment_method_types { for method_type in payment_method_types { diff --git a/crates/connector_configs/src/transformer.rs b/crates/connector_configs/src/transformer.rs index 1a54dc9ae0d4..fe39da67a938 100644 --- a/crates/connector_configs/src/transformer.rs +++ b/crates/connector_configs/src/transformer.rs @@ -138,6 +138,7 @@ impl DashboardRequestPayload { | PaymentMethod::Upi | PaymentMethod::Voucher | PaymentMethod::GiftCard + | PaymentMethod::OpenBanking | PaymentMethod::CardRedirect => { if let Some(provider) = payload.provider { let val = Self::transform_payment_method( diff --git a/crates/euclid/src/dssa/graph.rs b/crates/euclid/src/dssa/graph.rs index 008b56285125..13e115153829 100644 --- a/crates/euclid/src/dssa/graph.rs +++ b/crates/euclid/src/dssa/graph.rs @@ -69,6 +69,7 @@ impl cgraph::NodeViz for dir::DirValue { Self::SetupFutureUsage(sfu) => sfu.to_string(), Self::CardRedirectType(crt) => crt.to_string(), Self::RealTimePaymentType(rtpt) => rtpt.to_string(), + Self::OpenBankingType(ob) => ob.to_string(), } } } diff --git a/crates/euclid/src/frontend/ast/lowering.rs b/crates/euclid/src/frontend/ast/lowering.rs index 48a1a0ab8bf0..8a8ba695e9fa 100644 --- a/crates/euclid/src/frontend/ast/lowering.rs +++ b/crates/euclid/src/frontend/ast/lowering.rs @@ -264,6 +264,8 @@ fn lower_comparison_inner( dir::DirKeyKind::UpiType => lower_enum!(UpiType, value), + dir::DirKeyKind::OpenBankingType => lower_enum!(OpenBankingType, value), + dir::DirKeyKind::VoucherType => lower_enum!(VoucherType, value), dir::DirKeyKind::GiftCardType => lower_enum!(GiftCardType, value), diff --git a/crates/euclid/src/frontend/dir.rs b/crates/euclid/src/frontend/dir.rs index a21ae104882c..8cfdf6567166 100644 --- a/crates/euclid/src/frontend/dir.rs +++ b/crates/euclid/src/frontend/dir.rs @@ -319,6 +319,13 @@ pub enum DirKeyKind { props(Category = "Payment Method Types") )] RealTimePaymentType, + #[serde(rename = "open_banking")] + #[strum( + serialize = "open_banking", + detailed_message = "Supported types of open banking payment method", + props(Category = "Payment Method Types") + )] + OpenBankingType, } pub trait EuclidDirFilter: Sized @@ -367,6 +374,7 @@ impl DirKeyKind { Self::SetupFutureUsage => types::DataType::EnumVariant, Self::CardRedirectType => types::DataType::EnumVariant, Self::RealTimePaymentType => types::DataType::EnumVariant, + Self::OpenBankingType => types::DataType::EnumVariant, } } pub fn get_value_set(&self) -> Option> { @@ -498,6 +506,11 @@ impl DirKeyKind { .map(DirValue::RealTimePaymentType) .collect(), ), + Self::OpenBankingType => Some( + enums::OpenBankingType::iter() + .map(DirValue::OpenBankingType) + .collect(), + ), } } } @@ -565,6 +578,8 @@ pub enum DirValue { CardRedirectType(enums::CardRedirectType), #[serde(rename = "real_time_payment")] RealTimePaymentType(enums::RealTimePaymentType), + #[serde(rename = "open_banking")] + OpenBankingType(enums::OpenBankingType), } impl DirValue { @@ -599,6 +614,7 @@ impl DirValue { Self::VoucherType(_) => (DirKeyKind::VoucherType, None), Self::GiftCardType(_) => (DirKeyKind::GiftCardType, None), Self::RealTimePaymentType(_) => (DirKeyKind::RealTimePaymentType, None), + Self::OpenBankingType(_) => (DirKeyKind::OpenBankingType, None), }; DirKey::new(kind, data) @@ -634,6 +650,7 @@ impl DirValue { Self::SetupFutureUsage(_) => None, Self::CardRedirectType(_) => None, Self::RealTimePaymentType(_) => None, + Self::OpenBankingType(_) => None, } } diff --git a/crates/euclid/src/frontend/dir/enums.rs b/crates/euclid/src/frontend/dir/enums.rs index 5d0defc7357b..0d2f959c702c 100644 --- a/crates/euclid/src/frontend/dir/enums.rs +++ b/crates/euclid/src/frontend/dir/enums.rs @@ -160,6 +160,26 @@ pub enum BankRedirectType { Przelewy24, Trustly, } + +#[derive( + Clone, + Debug, + Hash, + PartialEq, + Eq, + strum::Display, + strum::VariantNames, + strum::EnumIter, + strum::EnumString, + serde::Serialize, + serde::Deserialize, +)] +#[serde(rename_all = "snake_case")] +#[strum(serialize_all = "snake_case")] +pub enum OpenBankingType { + OpenBankingPIS, +} + #[derive( Clone, Debug, @@ -350,3 +370,4 @@ collect_variants!(VoucherType); collect_variants!(GiftCardType); collect_variants!(BankTransferType); collect_variants!(CardRedirectType); +collect_variants!(OpenBankingType); diff --git a/crates/euclid/src/frontend/dir/lowering.rs b/crates/euclid/src/frontend/dir/lowering.rs index f91c63a8166e..33d368e96961 100644 --- a/crates/euclid/src/frontend/dir/lowering.rs +++ b/crates/euclid/src/frontend/dir/lowering.rs @@ -168,6 +168,14 @@ impl From for global_enums::PaymentMethodType { } } +impl From for global_enums::PaymentMethodType { + fn from(value: enums::OpenBankingType) -> Self { + match value { + enums::OpenBankingType::OpenBankingPIS => Self::OpenBankingPIS, + } + } +} + impl From for global_enums::PaymentMethodType { fn from(value: enums::CryptoType) -> Self { match value { @@ -238,6 +246,7 @@ fn lower_value(dir_value: dir::DirValue) -> Result EuclidValue::PaymentMethodType(rt.into()), dir::DirValue::BusinessLabel(bl) => EuclidValue::BusinessLabel(bl), dir::DirValue::SetupFutureUsage(sfu) => EuclidValue::SetupFutureUsage(sfu), + dir::DirValue::OpenBankingType(ob) => EuclidValue::PaymentMethodType(ob.into()), }) } diff --git a/crates/euclid/src/frontend/dir/transformers.rs b/crates/euclid/src/frontend/dir/transformers.rs index 3f83ee3db015..3e35bcd391c4 100644 --- a/crates/euclid/src/frontend/dir/transformers.rs +++ b/crates/euclid/src/frontend/dir/transformers.rs @@ -38,6 +38,7 @@ impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMet | global_enums::PaymentMethod::RealTimePayment | global_enums::PaymentMethod::Upi | global_enums::PaymentMethod::Voucher + | global_enums::PaymentMethod::OpenBanking | global_enums::PaymentMethod::GiftCard => Err(AnalysisErrorType::NotSupported), }, global_enums::PaymentMethodType::Bacs => match self.1 { @@ -53,6 +54,7 @@ impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMet | global_enums::PaymentMethod::RealTimePayment | global_enums::PaymentMethod::Upi | global_enums::PaymentMethod::Voucher + | global_enums::PaymentMethod::OpenBanking | global_enums::PaymentMethod::GiftCard => Err(AnalysisErrorType::NotSupported), }, global_enums::PaymentMethodType::Becs => Ok(dirval!(BankDebitType = Becs)), @@ -69,6 +71,7 @@ impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMet | global_enums::PaymentMethod::RealTimePayment | global_enums::PaymentMethod::Upi | global_enums::PaymentMethod::Voucher + | global_enums::PaymentMethod::OpenBanking | global_enums::PaymentMethod::GiftCard => Err(AnalysisErrorType::NotSupported), }, global_enums::PaymentMethodType::AliPay => Ok(dirval!(WalletType = AliPay)), @@ -182,6 +185,9 @@ impl IntoDirValue for (global_enums::PaymentMethodType, global_enums::PaymentMet } global_enums::PaymentMethodType::Venmo => Ok(dirval!(WalletType = Venmo)), global_enums::PaymentMethodType::Mifinity => Ok(dirval!(WalletType = Mifinity)), + global_enums::PaymentMethodType::OpenBankingPIS => { + Ok(dirval!(OpenBankingType = OpenBankingPIS)) + } } } } diff --git a/crates/euclid_wasm/src/lib.rs b/crates/euclid_wasm/src/lib.rs index e92552002b5d..031440ae5412 100644 --- a/crates/euclid_wasm/src/lib.rs +++ b/crates/euclid_wasm/src/lib.rs @@ -263,6 +263,7 @@ pub fn get_variant_values(key: &str) -> Result { dir::DirKeyKind::VoucherType => dir_enums::VoucherType::VARIANTS, dir::DirKeyKind::BankDebitType => dir_enums::BankDebitType::VARIANTS, dir::DirKeyKind::RealTimePaymentType => dir_enums::RealTimePaymentType::VARIANTS, + dir::DirKeyKind::OpenBankingType => dir_enums::OpenBankingType::VARIANTS, dir::DirKeyKind::PaymentAmount | dir::DirKeyKind::Connector diff --git a/crates/hyperswitch_domain_models/src/payment_method_data.rs b/crates/hyperswitch_domain_models/src/payment_method_data.rs index 005e21743d90..49b6951f5f8c 100644 --- a/crates/hyperswitch_domain_models/src/payment_method_data.rs +++ b/crates/hyperswitch_domain_models/src/payment_method_data.rs @@ -22,6 +22,7 @@ pub enum PaymentMethodData { Voucher(VoucherData), GiftCard(Box), CardToken(CardToken), + OpenBanking(OpenBankingData), } #[derive(Debug, Clone, PartialEq, Eq)] @@ -46,6 +47,7 @@ impl PaymentMethodData { Self::Upi(_) => Some(common_enums::PaymentMethod::Upi), Self::Voucher(_) => Some(common_enums::PaymentMethod::Voucher), Self::GiftCard(_) => Some(common_enums::PaymentMethod::GiftCard), + Self::OpenBanking(_) => Some(common_enums::PaymentMethod::OpenBanking), Self::CardToken(_) | Self::MandatePayment => None, } } @@ -318,6 +320,12 @@ pub enum BankRedirectData { LocalBankRedirect {}, } +#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum OpenBankingData { + OpenBankingPIS {}, +} + #[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)] #[serde(rename_all = "snake_case")] pub struct CryptoData { @@ -495,6 +503,9 @@ impl From for PaymentMethodData { api_models::payments::PaymentMethodData::CardToken(card_token) => { Self::CardToken(From::from(card_token)) } + api_models::payments::PaymentMethodData::OpenBanking(ob_data) => { + Self::OpenBanking(From::from(ob_data)) + } } } } @@ -922,3 +933,11 @@ impl From for RealTimePaymentData { } } } + +impl From for OpenBankingData { + fn from(value: api_models::payments::OpenBankingData) -> Self { + match value { + api_models::payments::OpenBankingData::OpenBankingPIS {} => Self::OpenBankingPIS {}, + } + } +} diff --git a/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs b/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs index 046866beea55..1263f8f975a6 100644 --- a/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs +++ b/crates/hyperswitch_domain_models/src/router_flow_types/payments.rs @@ -46,3 +46,6 @@ pub struct PreProcessing; #[derive(Debug, Clone)] pub struct IncrementalAuthorization; + +#[derive(Debug, Clone)] +pub struct PostProcessing; diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index 1c2d94bae1a1..825927d021ab 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -54,7 +54,7 @@ pub struct PaymentsAuthorizeData { pub payment_experience: Option, pub payment_method_type: Option, pub surcharge_details: Option, - pub customer_id: Option, + pub customer_id: Option, pub request_incremental_authorization: bool, pub metadata: Option, pub authentication_data: Option, @@ -336,6 +336,35 @@ impl TryFrom for PaymentsPreProcessingData { }) } } + +#[derive(Debug, Clone)] +pub struct PaymentsPostProcessingData { + pub payment_method_data: PaymentMethodData, + pub customer_id: Option, + pub connector_transaction_id: Option, +} + +impl TryFrom> + for PaymentsPostProcessingData +{ + type Error = error_stack::Report; + + fn try_from( + data: RouterData, + ) -> Result { + Ok(Self { + payment_method_data: data.request.payment_method_data, + connector_transaction_id: match data.response { + Ok(response_types::PaymentsResponseData::TransactionResponse { + resource_id: ResponseId::ConnectorTransactionId(id), + .. + }) => Some(id.clone()), + _ => None, + }, + customer_id: data.request.customer_id, + }) + } +} #[derive(Debug, Clone)] pub struct CompleteAuthorizeData { pub payment_method_data: Option, diff --git a/crates/hyperswitch_domain_models/src/router_response_types.rs b/crates/hyperswitch_domain_models/src/router_response_types.rs index a5c6aafdab13..e41b0aa78b9d 100644 --- a/crates/hyperswitch_domain_models/src/router_response_types.rs +++ b/crates/hyperswitch_domain_models/src/router_response_types.rs @@ -65,6 +65,9 @@ pub enum PaymentsResponseData { error_code: Option, error_message: Option, }, + PostProcessingResponse { + session_token: Option, + }, } #[derive(serde::Serialize, Debug, Clone)] diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 0cbf171495be..5b31a6be648e 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -143,6 +143,9 @@ fn get_dir_value_payment_method( api_enums::PaymentMethodType::DuitNow => Ok(dirval!(RealTimePaymentType = DuitNow)), api_enums::PaymentMethodType::PromptPay => Ok(dirval!(RealTimePaymentType = PromptPay)), api_enums::PaymentMethodType::VietQr => Ok(dirval!(RealTimePaymentType = VietQr)), + api_enums::PaymentMethodType::OpenBankingPIS => { + Ok(dirval!(OpenBankingType = OpenBankingPIS)) + } } } @@ -416,9 +419,11 @@ fn global_vec_pmt( global_vector.append(collect_global_variants!(GiftCardType)); global_vector.append(collect_global_variants!(BankTransferType)); global_vector.append(collect_global_variants!(CardRedirectType)); + global_vector.append(collect_global_variants!(OpenBankingType)); global_vector.push(dir::DirValue::PaymentMethod( dir::enums::PaymentMethod::Card, )); + let global_vector = global_vector .into_iter() .filter(|global_value| !enabled_pmt.contains(global_value)) diff --git a/crates/kgraph_utils/src/transformers.rs b/crates/kgraph_utils/src/transformers.rs index 1b1bd931200a..758a0dd3de03 100644 --- a/crates/kgraph_utils/src/transformers.rs +++ b/crates/kgraph_utils/src/transformers.rs @@ -103,6 +103,7 @@ impl IntoDirValue for api_enums::PaymentMethod { Self::Voucher => Ok(dirval!(PaymentMethod = Voucher)), Self::GiftCard => Ok(dirval!(PaymentMethod = GiftCard)), Self::CardRedirect => Ok(dirval!(PaymentMethod = CardRedirect)), + Self::OpenBanking => Ok(dirval!(PaymentMethod = OpenBanking)), } } } @@ -158,6 +159,7 @@ impl IntoDirValue for (api_enums::PaymentMethodType, api_enums::PaymentMethod) { | api_enums::PaymentMethod::RealTimePayment | api_enums::PaymentMethod::Upi | api_enums::PaymentMethod::Voucher + | api_enums::PaymentMethod::OpenBanking | api_enums::PaymentMethod::GiftCard => Err(KgraphError::ContextConstructionError( AnalysisErrorType::NotSupported, )), @@ -175,6 +177,7 @@ impl IntoDirValue for (api_enums::PaymentMethodType, api_enums::PaymentMethod) { | api_enums::PaymentMethod::RealTimePayment | api_enums::PaymentMethod::Upi | api_enums::PaymentMethod::Voucher + | api_enums::PaymentMethod::OpenBanking | api_enums::PaymentMethod::GiftCard => Err(KgraphError::ContextConstructionError( AnalysisErrorType::NotSupported, )), @@ -193,6 +196,7 @@ impl IntoDirValue for (api_enums::PaymentMethodType, api_enums::PaymentMethod) { | api_enums::PaymentMethod::RealTimePayment | api_enums::PaymentMethod::Upi | api_enums::PaymentMethod::Voucher + | api_enums::PaymentMethod::OpenBanking | api_enums::PaymentMethod::GiftCard => Err(KgraphError::ContextConstructionError( AnalysisErrorType::NotSupported, )), @@ -300,6 +304,9 @@ impl IntoDirValue for (api_enums::PaymentMethodType, api_enums::PaymentMethod) { api_enums::PaymentMethodType::DuitNow => Ok(dirval!(RealTimePaymentType = DuitNow)), api_enums::PaymentMethodType::PromptPay => Ok(dirval!(RealTimePaymentType = PromptPay)), api_enums::PaymentMethodType::VietQr => Ok(dirval!(RealTimePaymentType = VietQr)), + api_enums::PaymentMethodType::OpenBankingPIS => { + Ok(dirval!(OpenBankingType = OpenBankingPIS)) + } } } } diff --git a/crates/openapi/src/openapi.rs b/crates/openapi/src/openapi.rs index d2e669175eb3..233f43b08466 100644 --- a/crates/openapi/src/openapi.rs +++ b/crates/openapi/src/openapi.rs @@ -557,6 +557,8 @@ Never share your secret api keys. Keep them guarded and secure. api_models::payments::PaymentChargeResponse, api_models::refunds::ChargeRefunds, api_models::payments::CustomerDetailsResponse, + api_models::payments::OpenBankingData, + api_models::payments::OpenBankingSessionToken, )), modifiers(&SecurityAddon) )] diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs index 81dbbb748e38..62cf9416b24b 100644 --- a/crates/router/src/compatibility/stripe/payment_intents/types.rs +++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs @@ -864,6 +864,7 @@ pub(crate) fn into_stripe_next_action( payments::NextActionData::ThirdPartySdkSessionToken { session_token } => { StripeNextAction::ThirdPartySdkSessionToken { session_token } } + payments::NextActionData::QrCodeInformation { image_data_url, display_to_timestamp, diff --git a/crates/router/src/compatibility/stripe/setup_intents/types.rs b/crates/router/src/compatibility/stripe/setup_intents/types.rs index db5ada515299..b5408f600b47 100644 --- a/crates/router/src/compatibility/stripe/setup_intents/types.rs +++ b/crates/router/src/compatibility/stripe/setup_intents/types.rs @@ -418,6 +418,7 @@ pub(crate) fn into_stripe_next_action( payments::NextActionData::ThirdPartySdkSessionToken { session_token } => { StripeNextAction::ThirdPartySdkSessionToken { session_token } } + payments::NextActionData::QrCodeInformation { image_data_url, display_to_timestamp, diff --git a/crates/router/src/connector/aci/transformers.rs b/crates/router/src/connector/aci/transformers.rs index c9f81ec722bd..175138add758 100644 --- a/crates/router/src/connector/aci/transformers.rs +++ b/crates/router/src/connector/aci/transformers.rs @@ -447,6 +447,7 @@ impl TryFrom<&AciRouterData<&types::PaymentsAuthorizeRouterData>> for AciPayment | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Aci"), diff --git a/crates/router/src/connector/adyen.rs b/crates/router/src/connector/adyen.rs index bb22369f2b5e..96d17edc0e17 100644 --- a/crates/router/src/connector/adyen.rs +++ b/crates/router/src/connector/adyen.rs @@ -234,7 +234,8 @@ impl ConnectorValidation for Adyen { | PaymentMethodType::UpiIntent | PaymentMethodType::VietQr | PaymentMethodType::Mifinity - | PaymentMethodType::LocalBankRedirect => { + | PaymentMethodType::LocalBankRedirect + | PaymentMethodType::OpenBankingPIS => { capture_method_not_supported!(connector, capture_method, payment_method_type) } }, diff --git a/crates/router/src/connector/adyen/transformers.rs b/crates/router/src/connector/adyen/transformers.rs index fba42558ece0..693ec5fc8a65 100644 --- a/crates/router/src/connector/adyen/transformers.rs +++ b/crates/router/src/connector/adyen/transformers.rs @@ -1567,6 +1567,7 @@ impl<'a> TryFrom<&AdyenRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Reward | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Adyen"), @@ -2561,6 +2562,7 @@ impl<'a> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotSupported { message: "Network tokenization for payment method".to_string(), diff --git a/crates/router/src/connector/airwallex/transformers.rs b/crates/router/src/connector/airwallex/transformers.rs index fcb492bedfa9..9fab84559b54 100644 --- a/crates/router/src/connector/airwallex/transformers.rs +++ b/crates/router/src/connector/airwallex/transformers.rs @@ -204,6 +204,7 @@ impl TryFrom<&AirwallexRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("airwallex"), diff --git a/crates/router/src/connector/authorizedotnet/transformers.rs b/crates/router/src/connector/authorizedotnet/transformers.rs index 651054552f85..33570a782fd8 100644 --- a/crates/router/src/connector/authorizedotnet/transformers.rs +++ b/crates/router/src/connector/authorizedotnet/transformers.rs @@ -342,6 +342,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CreateCustomerProfileRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("authorizedotnet"), @@ -520,6 +521,7 @@ impl TryFrom<&AuthorizedotnetRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message( @@ -578,6 +580,7 @@ impl | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("authorizedotnet"), diff --git a/crates/router/src/connector/bambora/transformers.rs b/crates/router/src/connector/bambora/transformers.rs index 6bb96f5e9bfc..b2e9b1a032ef 100644 --- a/crates/router/src/connector/bambora/transformers.rs +++ b/crates/router/src/connector/bambora/transformers.rs @@ -185,6 +185,7 @@ impl TryFrom> for Bambora | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("bambora"), diff --git a/crates/router/src/connector/bankofamerica/transformers.rs b/crates/router/src/connector/bankofamerica/transformers.rs index c0434572ce16..34244bbb819b 100644 --- a/crates/router/src/connector/bankofamerica/transformers.rs +++ b/crates/router/src/connector/bankofamerica/transformers.rs @@ -321,6 +321,7 @@ impl TryFrom<&types::SetupMandateRouterData> for BankOfAmericaPaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("BankOfAmerica"), @@ -395,6 +396,7 @@ impl | common_enums::PaymentMethod::RealTimePayment | common_enums::PaymentMethod::Upi | common_enums::PaymentMethod::Voucher + | common_enums::PaymentMethod::OpenBanking | common_enums::PaymentMethod::GiftCard => None, }; @@ -1070,6 +1072,7 @@ impl TryFrom<&BankOfAmericaRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message( @@ -1557,6 +1560,7 @@ impl | common_enums::PaymentMethod::RealTimePayment | common_enums::PaymentMethod::Upi | common_enums::PaymentMethod::Voucher + | common_enums::PaymentMethod::OpenBanking | common_enums::PaymentMethod::GiftCard => None, }; @@ -1773,6 +1777,7 @@ impl | common_enums::PaymentMethod::RealTimePayment | common_enums::PaymentMethod::Upi | common_enums::PaymentMethod::Voucher + | common_enums::PaymentMethod::OpenBanking | common_enums::PaymentMethod::GiftCard => None, }; diff --git a/crates/router/src/connector/billwerk/transformers.rs b/crates/router/src/connector/billwerk/transformers.rs index c00303fc5e92..4a4521f31a02 100644 --- a/crates/router/src/connector/billwerk/transformers.rs +++ b/crates/router/src/connector/billwerk/transformers.rs @@ -103,6 +103,7 @@ impl TryFrom<&types::TokenizationRouterData> for BillwerkTokenRequest { | domain::payments::PaymentMethodData::Upi(_) | domain::payments::PaymentMethodData::Voucher(_) | domain::payments::PaymentMethodData::GiftCard(_) + | domain::payments::PaymentMethodData::OpenBanking(_) | domain::payments::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("billwerk"), diff --git a/crates/router/src/connector/bluesnap/transformers.rs b/crates/router/src/connector/bluesnap/transformers.rs index d1d5f1d8c9e8..f354bf1c2db7 100644 --- a/crates/router/src/connector/bluesnap/transformers.rs +++ b/crates/router/src/connector/bluesnap/transformers.rs @@ -233,6 +233,7 @@ impl TryFrom<&BluesnapRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( "Selected payment method via Token flow through bluesnap".to_string(), @@ -397,6 +398,7 @@ impl TryFrom<&BluesnapRouterData<&types::PaymentsAuthorizeRouterData>> for Blues | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("bluesnap"), diff --git a/crates/router/src/connector/boku/transformers.rs b/crates/router/src/connector/boku/transformers.rs index a1a239a6e1b0..a8b4d8c0ec2b 100644 --- a/crates/router/src/connector/boku/transformers.rs +++ b/crates/router/src/connector/boku/transformers.rs @@ -92,6 +92,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BokuPaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("boku"), diff --git a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs index d52328a7783b..af50bd10ce77 100644 --- a/crates/router/src/connector/braintree/braintree_graphql_transformers.rs +++ b/crates/router/src/connector/braintree/braintree_graphql_transformers.rs @@ -209,6 +209,7 @@ impl TryFrom<&BraintreeRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), @@ -241,6 +242,7 @@ impl TryFrom<&BraintreeRouterData<&types::PaymentsCompleteAuthorizeRouterData>> | api_models::enums::PaymentMethod::RealTimePayment | api_models::enums::PaymentMethod::Upi | api_models::enums::PaymentMethod::Voucher + | api_models::enums::PaymentMethod::OpenBanking | api_models::enums::PaymentMethod::GiftCard => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message( @@ -994,6 +996,7 @@ impl TryFrom<&types::TokenizationRouterData> for BraintreeTokenRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), @@ -1584,6 +1587,7 @@ fn get_braintree_redirect_form( | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => Err( errors::ConnectorError::NotImplemented("given payment method".to_owned()), )?, diff --git a/crates/router/src/connector/braintree/transformers.rs b/crates/router/src/connector/braintree/transformers.rs index 7fc34e75f979..717c4afc1956 100644 --- a/crates/router/src/connector/braintree/transformers.rs +++ b/crates/router/src/connector/braintree/transformers.rs @@ -175,6 +175,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for BraintreePaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("braintree"), diff --git a/crates/router/src/connector/checkout/transformers.rs b/crates/router/src/connector/checkout/transformers.rs index de8a8de98a9c..be48d2a864f6 100644 --- a/crates/router/src/connector/checkout/transformers.rs +++ b/crates/router/src/connector/checkout/transformers.rs @@ -132,6 +132,7 @@ impl TryFrom<&types::TokenizationRouterData> for TokenRequest { | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("checkout"), @@ -368,6 +369,7 @@ impl TryFrom<&CheckoutRouterData<&types::PaymentsAuthorizeRouterData>> for Payme | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("checkout"), diff --git a/crates/router/src/connector/cryptopay/transformers.rs b/crates/router/src/connector/cryptopay/transformers.rs index 215003f8fb2a..c15afd7fd444 100644 --- a/crates/router/src/connector/cryptopay/transformers.rs +++ b/crates/router/src/connector/cryptopay/transformers.rs @@ -78,6 +78,7 @@ impl TryFrom<&CryptopayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("CryptoPay"), diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 1cd6066ad697..2f286964e3e6 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -214,6 +214,7 @@ impl TryFrom<&types::SetupMandateRouterData> for CybersourceZeroMandateRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -1384,6 +1385,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -1490,6 +1492,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -2208,6 +2211,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsPreProcessingRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), @@ -2317,6 +2321,7 @@ impl TryFrom<&CybersourceRouterData<&types::PaymentsCompleteAuthorizeRouterData> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Cybersource"), diff --git a/crates/router/src/connector/datatrans/transformers.rs b/crates/router/src/connector/datatrans/transformers.rs index bb8c85b77da2..9c4ab6cf6a6f 100644 --- a/crates/router/src/connector/datatrans/transformers.rs +++ b/crates/router/src/connector/datatrans/transformers.rs @@ -193,6 +193,7 @@ impl TryFrom<&DatatransRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message("Datatrans"), diff --git a/crates/router/src/connector/dlocal/transformers.rs b/crates/router/src/connector/dlocal/transformers.rs index b018dbcd2e96..ca18d12a0883 100644 --- a/crates/router/src/connector/dlocal/transformers.rs +++ b/crates/router/src/connector/dlocal/transformers.rs @@ -166,6 +166,7 @@ impl TryFrom<&DlocalRouterData<&types::PaymentsAuthorizeRouterData>> for DlocalP | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( crate::connector::utils::get_unimplemented_payment_method_error_message( diff --git a/crates/router/src/connector/fiserv/transformers.rs b/crates/router/src/connector/fiserv/transformers.rs index eee518187a29..1a149c6af78b 100644 --- a/crates/router/src/connector/fiserv/transformers.rs +++ b/crates/router/src/connector/fiserv/transformers.rs @@ -188,6 +188,7 @@ impl TryFrom<&FiservRouterData<&types::PaymentsAuthorizeRouterData>> for FiservP | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("fiserv"), diff --git a/crates/router/src/connector/forte/transformers.rs b/crates/router/src/connector/forte/transformers.rs index 637a3acd1e17..0533d71ffeb3 100644 --- a/crates/router/src/connector/forte/transformers.rs +++ b/crates/router/src/connector/forte/transformers.rs @@ -115,6 +115,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for FortePaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Forte"), diff --git a/crates/router/src/connector/globepay/transformers.rs b/crates/router/src/connector/globepay/transformers.rs index 3cf1204a697c..8974c9f94b97 100644 --- a/crates/router/src/connector/globepay/transformers.rs +++ b/crates/router/src/connector/globepay/transformers.rs @@ -73,6 +73,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for GlobepayPaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("globepay"), diff --git a/crates/router/src/connector/gocardless/transformers.rs b/crates/router/src/connector/gocardless/transformers.rs index d31d985500ae..fb27f5138a9c 100644 --- a/crates/router/src/connector/gocardless/transformers.rs +++ b/crates/router/src/connector/gocardless/transformers.rs @@ -246,6 +246,7 @@ impl TryFrom<&types::TokenizationRouterData> for CustomerBankAccount { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Gocardless"), @@ -415,6 +416,7 @@ impl TryFrom<&types::SetupMandateRouterData> for GocardlessMandateRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( "Setup Mandate flow for selected payment method through Gocardless".to_string(), diff --git a/crates/router/src/connector/helcim/transformers.rs b/crates/router/src/connector/helcim/transformers.rs index f882b341f63b..2f6cf61e7591 100644 --- a/crates/router/src/connector/helcim/transformers.rs +++ b/crates/router/src/connector/helcim/transformers.rs @@ -163,6 +163,7 @@ impl TryFrom<&types::SetupMandateRouterData> for HelcimVerifyRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Helcim"), @@ -265,6 +266,7 @@ impl TryFrom<&HelcimRouterData<&types::PaymentsAuthorizeRouterData>> for HelcimP | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Helcim"), diff --git a/crates/router/src/connector/iatapay/transformers.rs b/crates/router/src/connector/iatapay/transformers.rs index 71aeed0f7dd0..1ce07fb49e32 100644 --- a/crates/router/src/connector/iatapay/transformers.rs +++ b/crates/router/src/connector/iatapay/transformers.rs @@ -204,7 +204,8 @@ impl | domain::PaymentMethodData::Reward | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::OpenBanking(_) => { Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("iatapay"), ))? diff --git a/crates/router/src/connector/klarna.rs b/crates/router/src/connector/klarna.rs index 4796f15cb64a..fbfb8b71974f 100644 --- a/crates/router/src/connector/klarna.rs +++ b/crates/router/src/connector/klarna.rs @@ -628,7 +628,8 @@ impl | common_enums::PaymentMethodType::Fps | common_enums::PaymentMethodType::DuitNow | common_enums::PaymentMethodType::PromptPay - | common_enums::PaymentMethodType::VietQr, + | common_enums::PaymentMethodType::VietQr + | common_enums::PaymentMethodType::OpenBankingPIS, ) => Err(error_stack::report!(errors::ConnectorError::NotSupported { message: payment_method_type.to_string(), connector: "klarna", @@ -649,6 +650,7 @@ impl | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardToken(_) => { Err(report!(errors::ConnectorError::NotImplemented( diff --git a/crates/router/src/connector/mifinity/transformers.rs b/crates/router/src/connector/mifinity/transformers.rs index 5d18834de86d..0e52f7d3b50f 100644 --- a/crates/router/src/connector/mifinity/transformers.rs +++ b/crates/router/src/connector/mifinity/transformers.rs @@ -196,6 +196,7 @@ impl TryFrom<&MifinityRouterData<&types::PaymentsAuthorizeRouterData>> for Mifin | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Mifinity"), diff --git a/crates/router/src/connector/multisafepay/transformers.rs b/crates/router/src/connector/multisafepay/transformers.rs index 2daf83cbacf1..ac0a04de182d 100644 --- a/crates/router/src/connector/multisafepay/transformers.rs +++ b/crates/router/src/connector/multisafepay/transformers.rs @@ -606,6 +606,7 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("multisafepay"), @@ -786,7 +787,8 @@ impl TryFrom<&MultisafepayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::OpenBanking(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("multisafepay"), ))? diff --git a/crates/router/src/connector/nexinets/transformers.rs b/crates/router/src/connector/nexinets/transformers.rs index c25da631b715..7e32ae5ebedb 100644 --- a/crates/router/src/connector/nexinets/transformers.rs +++ b/crates/router/src/connector/nexinets/transformers.rs @@ -625,6 +625,7 @@ fn get_payment_details_and_product( | PaymentMethodData::Upi(_) | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) + | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nexinets"), ))?, diff --git a/crates/router/src/connector/nmi/transformers.rs b/crates/router/src/connector/nmi/transformers.rs index a87b8e6176dd..e0858e16a4a6 100644 --- a/crates/router/src/connector/nmi/transformers.rs +++ b/crates/router/src/connector/nmi/transformers.rs @@ -584,6 +584,7 @@ impl | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nmi"), diff --git a/crates/router/src/connector/noon/transformers.rs b/crates/router/src/connector/noon/transformers.rs index 6de5ba5e6d12..8336cbf13188 100644 --- a/crates/router/src/connector/noon/transformers.rs +++ b/crates/router/src/connector/noon/transformers.rs @@ -352,6 +352,7 @@ impl TryFrom<&NoonRouterData<&types::PaymentsAuthorizeRouterData>> for NoonPayme | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( conn_utils::get_unimplemented_payment_method_error_message("Noon"), diff --git a/crates/router/src/connector/nuvei/transformers.rs b/crates/router/src/connector/nuvei/transformers.rs index a23a05e34e33..1aa345173570 100644 --- a/crates/router/src/connector/nuvei/transformers.rs +++ b/crates/router/src/connector/nuvei/transformers.rs @@ -995,6 +995,7 @@ where | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nuvei"), @@ -1196,6 +1197,7 @@ impl TryFrom<(&types::PaymentsCompleteAuthorizeRouterData, Secret)> | Some(domain::PaymentMethodData::Reward) | Some(domain::PaymentMethodData::RealTimePayment(..)) | Some(domain::PaymentMethodData::Upi(..)) + | Some(domain::PaymentMethodData::OpenBanking(_)) | Some(domain::PaymentMethodData::CardToken(..)) | None => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("nuvei"), diff --git a/crates/router/src/connector/opayo/transformers.rs b/crates/router/src/connector/opayo/transformers.rs index b6c05dff2d31..5771b45100bd 100644 --- a/crates/router/src/connector/opayo/transformers.rs +++ b/crates/router/src/connector/opayo/transformers.rs @@ -56,6 +56,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for OpayoPaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Opayo"), diff --git a/crates/router/src/connector/payeezy/transformers.rs b/crates/router/src/connector/payeezy/transformers.rs index c2d8e0dfddb9..8da3e7502bed 100644 --- a/crates/router/src/connector/payeezy/transformers.rs +++ b/crates/router/src/connector/payeezy/transformers.rs @@ -137,6 +137,7 @@ impl TryFrom<&PayeezyRouterData<&types::PaymentsAuthorizeRouterData>> for Payeez | diesel_models::enums::PaymentMethod::RealTimePayment | diesel_models::enums::PaymentMethod::Upi | diesel_models::enums::PaymentMethod::Voucher + | diesel_models::enums::PaymentMethod::OpenBanking | diesel_models::enums::PaymentMethod::GiftCard => { Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } @@ -259,6 +260,7 @@ fn get_payment_method_data( | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Payeezy"), ))?, diff --git a/crates/router/src/connector/payme/transformers.rs b/crates/router/src/connector/payme/transformers.rs index 47942d7de43e..2ec35b7a7de0 100644 --- a/crates/router/src/connector/payme/transformers.rs +++ b/crates/router/src/connector/payme/transformers.rs @@ -429,6 +429,7 @@ impl TryFrom<&PaymentMethodData> for SalePaymentMethod { | PaymentMethodData::CardRedirect(_) | PaymentMethodData::Upi(_) | PaymentMethodData::Voucher(_) + | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } @@ -673,6 +674,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PayRequest { | PaymentMethodData::Upi(_) | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) + | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("payme"), ))?, @@ -732,6 +734,7 @@ impl TryFrom<&types::PaymentsCompleteAuthorizeRouterData> for Pay3dsRequest { | Some(PaymentMethodData::Upi(_)) | Some(PaymentMethodData::Voucher(_)) | Some(PaymentMethodData::GiftCard(_)) + | Some(PaymentMethodData::OpenBanking(_)) | Some(PaymentMethodData::CardToken(_)) | None => { Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into()) @@ -771,6 +774,7 @@ impl TryFrom<&types::TokenizationRouterData> for CaptureBuyerRequest { | PaymentMethodData::Upi(_) | PaymentMethodData::Voucher(_) | PaymentMethodData::GiftCard(_) + | PaymentMethodData::OpenBanking(_) | PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented("Tokenize Flow".to_string()).into()) } diff --git a/crates/router/src/connector/paypal/transformers.rs b/crates/router/src/connector/paypal/transformers.rs index 283b6c80a8e6..9718fd5a8ded 100644 --- a/crates/router/src/connector/paypal/transformers.rs +++ b/crates/router/src/connector/paypal/transformers.rs @@ -551,6 +551,7 @@ impl TryFrom<&PaypalRouterData<&types::PaymentsAuthorizeRouterData>> for PaypalP | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Crypto(_) | domain::PaymentMethodData::Upi(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Paypal"), diff --git a/crates/router/src/connector/placetopay/transformers.rs b/crates/router/src/connector/placetopay/transformers.rs index 50813a816299..6ecaabd3fe6c 100644 --- a/crates/router/src/connector/placetopay/transformers.rs +++ b/crates/router/src/connector/placetopay/transformers.rs @@ -151,6 +151,7 @@ impl TryFrom<&PlacetopayRouterData<&types::PaymentsAuthorizeRouterData>> | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Placetopay"), diff --git a/crates/router/src/connector/powertranz/transformers.rs b/crates/router/src/connector/powertranz/transformers.rs index 6fe1a7173be4..40fe8235956b 100644 --- a/crates/router/src/connector/powertranz/transformers.rs +++ b/crates/router/src/connector/powertranz/transformers.rs @@ -116,6 +116,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for PowertranzPaymentsRequest | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotSupported { message: utils::SELECTED_PAYMENT_METHOD.to_string(), diff --git a/crates/router/src/connector/razorpay/transformers.rs b/crates/router/src/connector/razorpay/transformers.rs index 5618c85d531a..8cf328918868 100644 --- a/crates/router/src/connector/razorpay/transformers.rs +++ b/crates/router/src/connector/razorpay/transformers.rs @@ -398,6 +398,7 @@ impl | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()) } diff --git a/crates/router/src/connector/shift4/transformers.rs b/crates/router/src/connector/shift4/transformers.rs index 80ef0daaf21e..f384d8d296d5 100644 --- a/crates/router/src/connector/shift4/transformers.rs +++ b/crates/router/src/connector/shift4/transformers.rs @@ -247,6 +247,7 @@ where | domain::PaymentMethodData::Reward | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Shift4"), @@ -469,6 +470,7 @@ impl TryFrom<&types::RouterData Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Shift4"), diff --git a/crates/router/src/connector/square/transformers.rs b/crates/router/src/connector/square/transformers.rs index 15de18c88904..67be82bb2d91 100644 --- a/crates/router/src/connector/square/transformers.rs +++ b/crates/router/src/connector/square/transformers.rs @@ -174,6 +174,7 @@ impl TryFrom<&types::TokenizationRouterData> for SquareTokenRequest { | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Square"), @@ -290,6 +291,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for SquarePaymentsRequest { | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Square"), diff --git a/crates/router/src/connector/stax/transformers.rs b/crates/router/src/connector/stax/transformers.rs index 65ee90830a36..d814deef264c 100644 --- a/crates/router/src/connector/stax/transformers.rs +++ b/crates/router/src/connector/stax/transformers.rs @@ -108,6 +108,7 @@ impl TryFrom<&StaxRouterData<&types::PaymentsAuthorizeRouterData>> for StaxPayme | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Upi(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Stax"), @@ -260,6 +261,7 @@ impl TryFrom<&types::TokenizationRouterData> for StaxTokenRequest { | domain::PaymentMethodData::GiftCard(_) | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Upi(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Stax"), diff --git a/crates/router/src/connector/stripe/transformers.rs b/crates/router/src/connector/stripe/transformers.rs index aa792db04bd4..2524a1aeacff 100644 --- a/crates/router/src/connector/stripe/transformers.rs +++ b/crates/router/src/connector/stripe/transformers.rs @@ -704,6 +704,7 @@ impl TryFrom for StripePaymentMethodType { | enums::PaymentMethodType::OnlineBankingPoland | enums::PaymentMethodType::OnlineBankingSlovakia | enums::PaymentMethodType::OpenBankingUk + | enums::PaymentMethodType::OpenBankingPIS | enums::PaymentMethodType::PagoEfectivo | enums::PaymentMethodType::PayBright | enums::PaymentMethodType::Pse @@ -1330,6 +1331,7 @@ fn create_stripe_payment_method( domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::MandatePayment + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), ) @@ -1706,6 +1708,7 @@ impl TryFrom<(&types::PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntent | domain::payments::PaymentMethodData::Upi(_) | domain::payments::PaymentMethodData::Voucher(_) | domain::payments::PaymentMethodData::GiftCard(_) + | domain::payments::PaymentMethodData::OpenBanking(_) | domain::payments::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotSupported { message: "Network tokenization for payment method".to_string(), @@ -3281,6 +3284,7 @@ impl | Some(domain::PaymentMethodData::GiftCard(..)) | Some(domain::PaymentMethodData::CardRedirect(..)) | Some(domain::PaymentMethodData::Voucher(..)) + | Some(domain::PaymentMethodData::OpenBanking(..)) | Some(domain::PaymentMethodData::CardToken(..)) | None => Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), @@ -3734,6 +3738,7 @@ impl | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::Voucher(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( connector_util::get_unimplemented_payment_method_error_message("stripe"), diff --git a/crates/router/src/connector/trustpay/transformers.rs b/crates/router/src/connector/trustpay/transformers.rs index 77d85393d29f..85539e253b7b 100644 --- a/crates/router/src/connector/trustpay/transformers.rs +++ b/crates/router/src/connector/trustpay/transformers.rs @@ -433,6 +433,7 @@ impl TryFrom<&TrustpayRouterData<&types::PaymentsAuthorizeRouterData>> for Trust | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("trustpay"), diff --git a/crates/router/src/connector/tsys/transformers.rs b/crates/router/src/connector/tsys/transformers.rs index cea51ce77e19..53561d93a926 100644 --- a/crates/router/src/connector/tsys/transformers.rs +++ b/crates/router/src/connector/tsys/transformers.rs @@ -76,6 +76,7 @@ impl TryFrom<&types::PaymentsAuthorizeRouterData> for TsysPaymentsRequest { | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("tsys"), diff --git a/crates/router/src/connector/utils.rs b/crates/router/src/connector/utils.rs index 6009fe51d59d..cdcc8411d917 100644 --- a/crates/router/src/connector/utils.rs +++ b/crates/router/src/connector/utils.rs @@ -2670,6 +2670,7 @@ pub enum PaymentMethodDataType { Fps, PromptPay, VietQr, + OpenBanking, } impl From for PaymentMethodDataType { @@ -2850,6 +2851,9 @@ impl From for PaymentMethodDataType { } } domain::payments::PaymentMethodData::CardToken(_) => Self::CardToken, + domain::payments::PaymentMethodData::OpenBanking(data) => match data { + hyperswitch_domain_models::payment_method_data::OpenBankingData::OpenBankingPIS { } => Self::OpenBanking + }, } } } diff --git a/crates/router/src/connector/volt/transformers.rs b/crates/router/src/connector/volt/transformers.rs index 12064fdf0f40..ac55f55d5369 100644 --- a/crates/router/src/connector/volt/transformers.rs +++ b/crates/router/src/connector/volt/transformers.rs @@ -150,6 +150,7 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Volt"), diff --git a/crates/router/src/connector/worldline/transformers.rs b/crates/router/src/connector/worldline/transformers.rs index fe1bcab0bb23..365ddda4d537 100644 --- a/crates/router/src/connector/worldline/transformers.rs +++ b/crates/router/src/connector/worldline/transformers.rs @@ -249,6 +249,7 @@ impl | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("worldline"), diff --git a/crates/router/src/connector/worldpay/transformers.rs b/crates/router/src/connector/worldpay/transformers.rs index 5caf6b0e1f16..892b66447228 100644 --- a/crates/router/src/connector/worldpay/transformers.rs +++ b/crates/router/src/connector/worldpay/transformers.rs @@ -108,6 +108,7 @@ fn fetch_payment_instrument( | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::CardRedirect(_) | domain::PaymentMethodData::GiftCard(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("worldpay"), ) diff --git a/crates/router/src/connector/zen/transformers.rs b/crates/router/src/connector/zen/transformers.rs index 026c39f0e3e9..2562277ee9b4 100644 --- a/crates/router/src/connector/zen/transformers.rs +++ b/crates/router/src/connector/zen/transformers.rs @@ -694,6 +694,7 @@ impl TryFrom<&ZenRouterData<&types::PaymentsAuthorizeRouterData>> for ZenPayment | domain::PaymentMethodData::Reward | domain::PaymentMethodData::RealTimePayment(_) | domain::PaymentMethodData::Upi(_) + | domain::PaymentMethodData::OpenBanking(_) | domain::PaymentMethodData::CardToken(_) => { Err(errors::ConnectorError::NotImplemented( utils::get_unimplemented_payment_method_error_message("Zen"), diff --git a/crates/router/src/connector/zsl/transformers.rs b/crates/router/src/connector/zsl/transformers.rs index f44d46cd8e07..ada1de0e6188 100644 --- a/crates/router/src/connector/zsl/transformers.rs +++ b/crates/router/src/connector/zsl/transformers.rs @@ -181,7 +181,8 @@ impl TryFrom<&ZslRouterData<&types::PaymentsAuthorizeRouterData>> for ZslPayment | domain::PaymentMethodData::Upi(_) | domain::PaymentMethodData::Voucher(_) | domain::PaymentMethodData::GiftCard(_) - | domain::PaymentMethodData::CardToken(_) => { + | domain::PaymentMethodData::CardToken(_) + | domain::PaymentMethodData::OpenBanking(_) => { Err(errors::ConnectorError::NotImplemented( connector_utils::get_unimplemented_payment_method_error_message( item.router_data.connector.as_str(), diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 0d0eace13e3e..331eb4302117 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -91,6 +91,7 @@ where key_store, customer, &merchant_connector_account, + None, ) .await?; diff --git a/crates/router/src/core/fraud_check/flows/checkout_flow.rs b/crates/router/src/core/fraud_check/flows/checkout_flow.rs index d897b9e6339e..47ab1810f75f 100644 --- a/crates/router/src/core/fraud_check/flows/checkout_flow.rs +++ b/crates/router/src/core/fraud_check/flows/checkout_flow.rs @@ -13,11 +13,14 @@ use crate::{ }, errors, services, types::{ - api::fraud_check::{self as frm_api, FraudCheckConnectorData}, + api::{ + self, + fraud_check::{self as frm_api, FraudCheckConnectorData}, + }, domain, fraud_check::{FraudCheckCheckoutData, FraudCheckResponseData, FrmCheckoutRouterData}, storage::enums as storage_enums, - BrowserInformation, ConnectorAuthType, ResponseId, RouterData, + BrowserInformation, ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData, }, SessionState, }; @@ -34,6 +37,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, + _merchant_recipient_data: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -135,6 +139,17 @@ impl ConstructFlowSpecificData( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/fraud_check/flows/record_return.rs b/crates/router/src/core/fraud_check/flows/record_return.rs index b44f2d3d4fc8..2f2631240835 100644 --- a/crates/router/src/core/fraud_check/flows/record_return.rs +++ b/crates/router/src/core/fraud_check/flows/record_return.rs @@ -11,13 +11,13 @@ use crate::{ }, errors, services, types::{ - api::RecordReturn, + api::{self, RecordReturn}, domain, fraud_check::{ FraudCheckRecordReturnData, FraudCheckResponseData, FrmRecordReturnRouterData, }, storage::enums as storage_enums, - ConnectorAuthType, ResponseId, RouterData, + ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData, }, utils, SessionState, }; @@ -34,6 +34,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, + _merchant_recipient_data: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -107,6 +108,17 @@ impl ConstructFlowSpecificData( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/fraud_check/flows/sale_flow.rs b/crates/router/src/core/fraud_check/flows/sale_flow.rs index 907b941ee67c..fee1964a42a2 100644 --- a/crates/router/src/core/fraud_check/flows/sale_flow.rs +++ b/crates/router/src/core/fraud_check/flows/sale_flow.rs @@ -11,11 +11,11 @@ use crate::{ }, errors, services, types::{ - api::fraud_check as frm_api, + api::{self, fraud_check as frm_api}, domain, fraud_check::{FraudCheckResponseData, FraudCheckSaleData, FrmSaleRouterData}, storage::enums as storage_enums, - ConnectorAuthType, ResponseId, RouterData, + ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData, }, SessionState, }; @@ -32,6 +32,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, + _merchant_recipient_data: Option, ) -> RouterResult> { let status = storage_enums::AttemptStatus::Pending; @@ -116,6 +117,17 @@ impl ConstructFlowSpecificData( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/fraud_check/flows/transaction_flow.rs b/crates/router/src/core/fraud_check/flows/transaction_flow.rs index dd042a6b5939..c5d00c4a8e97 100644 --- a/crates/router/src/core/fraud_check/flows/transaction_flow.rs +++ b/crates/router/src/core/fraud_check/flows/transaction_flow.rs @@ -10,13 +10,13 @@ use crate::{ }, errors, services, types::{ - api::fraud_check as frm_api, + api::{self, fraud_check as frm_api}, domain, fraud_check::{ FraudCheckResponseData, FraudCheckTransactionData, FrmTransactionRouterData, }, storage::enums as storage_enums, - ConnectorAuthType, ResponseId, RouterData, + ConnectorAuthType, MerchantRecipientData, ResponseId, RouterData, }, SessionState, }; @@ -37,6 +37,7 @@ impl _key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + _merchant_recipient_data: Option, ) -> RouterResult< RouterData, > { @@ -120,6 +121,17 @@ impl Ok(router_data) } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index a7a87b6d96e7..00b51b9f09e2 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -71,6 +71,7 @@ pub async fn retrieve_payment_method( pm @ Some(api::PaymentMethodData::RealTimePayment(_)) => Ok((pm.to_owned(), None)), pm @ Some(api::PaymentMethodData::CardRedirect(_)) => Ok((pm.to_owned(), None)), pm @ Some(api::PaymentMethodData::GiftCard(_)) => Ok((pm.to_owned(), None)), + pm @ Some(api::PaymentMethodData::OpenBanking(_)) => Ok((pm.to_owned(), None)), pm_opt @ Some(pm @ api::PaymentMethodData::BankTransfer(_)) => { let payment_token = helpers::store_payment_method_data_in_vault( state, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index acfebf93046b..c6a836f9c47e 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -27,7 +27,7 @@ use api_models::{ }; use common_utils::{ ext_traits::{AsyncExt, StringExt}, - pii, + id_type, pii, types::{MinorUnit, Surcharge}, }; use diesel_models::{ephemeral_key, fraud_check::FraudCheck}; @@ -41,7 +41,7 @@ pub use hyperswitch_domain_models::{ router_data::RouterData, router_request_types::CustomerDetails, }; -use masking::{ExposeInterface, Secret}; +use masking::{ExposeInterface, PeekInterface, Secret}; use redis_interface::errors::RedisError; use router_env::{instrument, metrics::add_attributes, tracing}; #[cfg(feature = "olap")] @@ -74,6 +74,7 @@ use crate::{ core::{ authentication as authentication_core, errors::{self, CustomResult, RouterResponse, RouterResult}, + payment_methods::cards, utils, }, db::StorageInterface, @@ -285,7 +286,7 @@ where } else { None }; - let router_data = call_connector_service( + let (router_data, mca) = call_connector_service( state, req_state.clone(), &merchant_account, @@ -307,6 +308,9 @@ where ) .await?; + let op_ref = &operation; + let should_trigger_post_processing_flows = is_operation_confirm(&operation); + let operation = Box::new(PaymentResponse); connector_http_status_code = router_data.connector_http_status_code; @@ -326,7 +330,7 @@ where ) .await?; - operation + let mut payment_data = operation .to_post_update_tracker()? .update_tracker( state, @@ -336,7 +340,23 @@ where &key_store, merchant_account.storage_scheme, ) - .await? + .await?; + + if should_trigger_post_processing_flows { + complete_postprocessing_steps_if_required( + state, + &merchant_account, + &key_store, + &customer, + &mca, + &connector, + &mut payment_data, + op_ref, + ) + .await?; + } + + payment_data } ConnectorCallType::Retryable(connectors) => { @@ -357,7 +377,7 @@ where } else { None }; - let router_data = call_connector_service( + let (router_data, mca) = call_connector_service( state, req_state.clone(), &merchant_account, @@ -414,6 +434,9 @@ where }; } + let op_ref = &operation; + let should_trigger_post_processing_flows = is_operation_confirm(&operation); + let operation = Box::new(PaymentResponse); connector_http_status_code = router_data.connector_http_status_code; external_latency = router_data.external_latency; @@ -432,7 +455,7 @@ where ) .await?; - operation + let mut payment_data = operation .to_post_update_tracker()? .update_tracker( state, @@ -442,7 +465,23 @@ where &key_store, merchant_account.storage_scheme, ) - .await? + .await?; + + if should_trigger_post_processing_flows { + complete_postprocessing_steps_if_required( + state, + &merchant_account, + &key_store, + &customer, + &mca, + &connector_data, + &mut payment_data, + op_ref, + ) + .await?; + } + + payment_data } ConnectorCallType::SessionMultiple(connectors) => { @@ -1400,7 +1439,10 @@ pub async fn call_connector_service( frm_suggestion: Option, business_profile: &storage::business_profile::BusinessProfile, is_retry_payment: bool, -) -> RouterResult> +) -> RouterResult<( + RouterData, + helpers::MerchantConnectorAccountType, +)> where F: Send + Clone + Sync, RouterDReq: Send + Sync, @@ -1461,6 +1503,16 @@ where ) .await?; + let merchant_recipient_data = payment_data + .get_merchant_recipient_data( + state, + merchant_account, + key_store, + &merchant_connector_account, + &connector, + ) + .await?; + let mut router_data = payment_data .construct_router_data( state, @@ -1469,6 +1521,7 @@ where key_store, customer, &merchant_connector_account, + merchant_recipient_data, ) .await?; @@ -1611,7 +1664,7 @@ where ) .await?; - let router_data_res = if should_continue_further { + let router_data = if should_continue_further { // The status of payment_attempt and intent will be updated in the previous step // update this in router_data. // This is added because few connector integrations do not update the status, @@ -1629,13 +1682,75 @@ where .await } else { Ok(router_data) - }; + }?; let etime_connector = Instant::now(); let duration_connector = etime_connector.saturating_duration_since(stime_connector); tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis())); - router_data_res + Ok((router_data, merchant_connector_account)) +} + +pub async fn get_merchant_bank_data_for_open_banking_connectors( + merchant_connector_account: &helpers::MerchantConnectorAccountType, + key_store: &domain::MerchantKeyStore, + connector: &api::ConnectorData, + state: &SessionState, + merchant_account: &domain::MerchantAccount, +) -> RouterResult> { + let merchant_data = merchant_connector_account + .get_additional_merchant_data() + .get_required_value("additional_merchant_data")? + .into_inner() + .peek() + .clone(); + + let merchant_recipient_data = merchant_data + .parse_value::("AdditionalMerchantData") + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("failed to decode MerchantRecipientData")?; + + let connector_name = enums::Connector::to_string(&connector.connector_name); + let locker_based_connector_list = state.conf.locker_based_open_banking_connectors.clone(); + let contains = locker_based_connector_list + .connector_list + .contains(connector_name.as_str()); + + let recipient_id = helpers::get_recipient_id_for_open_banking(&merchant_recipient_data)?; + let final_recipient_data = if let Some(id) = recipient_id { + if contains { + // Customer Id for OpenBanking connectors will be merchant_id as the account data stored at locker belongs to the merchant + let cust_id = id_type::CustomerId::from(merchant_account.merchant_id.clone().into()) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to convert to CustomerId")?; + let locker_resp = cards::get_payment_method_from_hs_locker( + state, + key_store, + &cust_id, + merchant_account.merchant_id.as_str(), + id.as_str(), + Some(enums::LockerChoice::HyperswitchCardVault), + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Merchant bank account data could not be fetched from locker")?; + + let parsed: router_types::MerchantAccountData = locker_resp + .peek() + .to_string() + .parse_struct("MerchantAccountData") + .change_context(errors::ApiErrorResponse::InternalServerError)?; + + Some(router_types::MerchantRecipientData::AccountData(parsed)) + } else { + Some(router_types::MerchantRecipientData::ConnectorRecipientId( + Secret::new(id), + )) + } + } else { + None + }; + Ok(final_recipient_data) } async fn blocklist_guard( @@ -1743,6 +1858,7 @@ where key_store, customer, &merchant_connector_account, + None, ) .await?; @@ -1875,6 +1991,7 @@ where key_store, customer, merchant_connector_account, + None, ) .await?; @@ -2043,6 +2160,69 @@ where Ok(router_data_and_should_continue_payment) } +#[allow(clippy::too_many_arguments)] +async fn complete_postprocessing_steps_if_required( + state: &SessionState, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + customer: &Option, + merchant_conn_account: &helpers::MerchantConnectorAccountType, + connector: &api::ConnectorData, + payment_data: &mut PaymentData, + _operation: &BoxedOperation<'_, F, Q>, +) -> RouterResult> +where + F: Send + Clone + Sync, + RouterDReq: Send + Sync, + + RouterData: Feature + Send, + dyn api::Connector: + services::api::ConnectorIntegration, + PaymentData: ConstructFlowSpecificData, +{ + let mut router_data = payment_data + .construct_router_data( + state, + connector.connector.id(), + merchant_account, + key_store, + customer, + merchant_conn_account, + None, + ) + .await?; + + match payment_data.payment_method_data.clone() { + Some(api_models::payments::PaymentMethodData::OpenBanking( + api_models::payments::OpenBankingData::OpenBankingPIS { .. }, + )) => { + if connector.connector_name == router_types::Connector::Plaid { + router_data = router_data.postprocessing_steps(state, connector).await?; + let token = if let Ok(ref res) = router_data.response { + match res { + router_types::PaymentsResponseData::PostProcessingResponse { + session_token, + } => session_token + .as_ref() + .map(|token| api::SessionToken::OpenBanking(token.clone())), + _ => None, + } + } else { + None + }; + if let Some(t) = token { + payment_data.sessions_token.push(t); + } + + Ok(router_data) + } else { + Ok(router_data) + } + } + _ => Ok(router_data), + } +} + pub fn is_preprocessing_required_for_wallets(connector_name: String) -> bool { connector_name == *"trustpay" || connector_name == *"payme" } diff --git a/crates/router/src/core/payments/connector_integration_v2_impls.rs b/crates/router/src/core/payments/connector_integration_v2_impls.rs index 178b14dde544..fbedca14aeba 100644 --- a/crates/router/src/core/payments/connector_integration_v2_impls.rs +++ b/crates/router/src/core/payments/connector_integration_v2_impls.rs @@ -40,6 +40,8 @@ mod dummy_connector_default_impl { impl api::PaymentsPreProcessingV2 for connector::DummyConnector {} + impl api::PaymentsPostProcessingV2 for connector::DummyConnector {} + impl services::ConnectorIntegrationV2< api::Authorize, @@ -170,6 +172,16 @@ mod dummy_connector_default_impl { { } + impl + services::ConnectorIntegrationV2< + api::PostProcessing, + types::PaymentFlowData, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, + > for connector::DummyConnector + { + } + impl services::ConnectorIntegrationV2< api::AuthorizeSessionToken, @@ -544,6 +556,7 @@ macro_rules! default_imp_for_new_connector_integration_payment { impl api::PaymentTokenV2 for $path::$connector{} impl api::ConnectorCustomerV2 for $path::$connector{} impl api::PaymentsPreProcessingV2 for $path::$connector{} + impl api::PaymentsPostProcessingV2 for $path::$connector{} impl services::ConnectorIntegrationV2 for $path::$connector{} @@ -603,6 +616,12 @@ macro_rules! default_imp_for_new_connector_integration_payment { types::PaymentsPreProcessingData, types::PaymentsResponseData, > for $path::$connector{} + impl services::ConnectorIntegrationV2< + api::PostProcessing, + types::PaymentFlowData, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, + > for $path::$connector{} impl services::ConnectorIntegrationV2< api::AuthorizeSessionToken, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 815b02285dd7..53ca8d02451e 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -25,6 +25,7 @@ use crate::{ }; #[async_trait] +#[allow(clippy::too_many_arguments)] pub trait ConstructFlowSpecificData { async fn construct_router_data<'a>( &self, @@ -34,7 +35,17 @@ pub trait ConstructFlowSpecificData { key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult>; + + async fn get_merchant_recipient_data<'a>( + &self, + state: &SessionState, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + merchant_connector_account: &helpers::MerchantConnectorAccountType, + connector: &api::ConnectorData, + ) -> RouterResult>; } #[allow(clippy::too_many_arguments)] @@ -110,6 +121,19 @@ pub trait Feature { Ok(self) } + async fn postprocessing_steps<'a>( + self, + _state: &SessionState, + _connector: &api::ConnectorData, + ) -> RouterResult + where + F: Clone, + Self: Sized, + dyn api::Connector: services::ConnectorIntegration, + { + Ok(self) + } + async fn create_connector_customer<'a>( &self, _state: &SessionState, @@ -970,6 +994,21 @@ macro_rules! default_imp_for_pre_processing_steps{ }; } +macro_rules! default_imp_for_post_processing_steps{ + ($($path:ident::$connector:ident),*)=> { + $( + impl api::PaymentsPostProcessing for $path::$connector {} + impl + services::ConnectorIntegration< + api::PostProcessing, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, + > for $path::$connector + {} + )* + }; +} + #[cfg(feature = "dummy_connector")] impl api::PaymentsPreProcessing for connector::DummyConnector {} #[cfg(feature = "dummy_connector")] @@ -1039,6 +1078,86 @@ default_imp_for_pre_processing_steps!( connector::Zsl ); +#[cfg(feature = "dummy_connector")] +impl api::PaymentsPostProcessing for connector::DummyConnector {} +#[cfg(feature = "dummy_connector")] +impl + services::ConnectorIntegration< + api::PostProcessing, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, + > for connector::DummyConnector +{ +} + +default_imp_for_post_processing_steps!( + connector::Adyenplatform, + connector::Adyen, + connector::Airwallex, + connector::Bankofamerica, + connector::Cybersource, + connector::Gocardless, + connector::Nmi, + connector::Nuvei, + connector::Payme, + connector::Paypal, + connector::Shift4, + connector::Stripe, + connector::Trustpay, + connector::Aci, + connector::Authorizedotnet, + connector::Bambora, + connector::Bamboraapac, + connector::Billwerk, + connector::Bitpay, + connector::Bluesnap, + connector::Boku, + connector::Braintree, + connector::Cashtocode, + connector::Checkout, + connector::Coinbase, + connector::Cryptopay, + connector::Datatrans, + connector::Dlocal, + connector::Ebanx, + connector::Iatapay, + connector::Fiserv, + connector::Forte, + connector::Globalpay, + connector::Globepay, + connector::Gpayments, + connector::Helcim, + connector::Klarna, + connector::Mifinity, + connector::Mollie, + connector::Multisafepay, + connector::Netcetera, + connector::Nexinets, + connector::Noon, + connector::Opayo, + connector::Opennode, + connector::Payeezy, + connector::Payone, + connector::Payu, + connector::Placetopay, + connector::Powertranz, + connector::Prophetpay, + connector::Rapyd, + connector::Riskified, + connector::Signifyd, + connector::Square, + connector::Stax, + connector::Threedsecureio, + connector::Tsys, + connector::Volt, + connector::Wise, + connector::Worldline, + connector::Worldpay, + connector::Zen, + connector::Zsl, + connector::Razorpay +); + macro_rules! default_imp_for_payouts { ($($path:ident::$connector:ident),*) => { $( diff --git a/crates/router/src/core/payments/flows/approve_flow.rs b/crates/router/src/core/payments/flows/approve_flow.rs index f4f8409912e5..63224f5b0cf0 100644 --- a/crates/router/src/core/payments/flows/approve_flow.rs +++ b/crates/router/src/core/payments/flows/approve_flow.rs @@ -24,6 +24,7 @@ impl key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Approve, @@ -36,9 +37,21 @@ impl key_store, customer, merchant_connector_account, + merchant_recipient_data, )) .await } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/authorize_flow.rs b/crates/router/src/core/payments/flows/authorize_flow.rs index 50cf31730a99..db34c40d928a 100644 --- a/crates/router/src/core/payments/flows/authorize_flow.rs +++ b/crates/router/src/core/payments/flows/authorize_flow.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use common_enums as enums; use router_env::metrics::add_attributes; // use router_env::tracing::Instrument; @@ -16,6 +17,7 @@ use crate::{ services, services::api::ConnectorValidation, types::{self, api, domain, storage, transformers::ForeignFrom}, + utils::OptionExt, }; #[async_trait] @@ -34,6 +36,7 @@ impl key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult< types::RouterData< api::Authorize, @@ -52,9 +55,39 @@ impl key_store, customer, merchant_connector_account, + merchant_recipient_data, )) .await } + + async fn get_merchant_recipient_data<'a>( + &self, + state: &SessionState, + merchant_account: &domain::MerchantAccount, + key_store: &domain::MerchantKeyStore, + merchant_connector_account: &helpers::MerchantConnectorAccountType, + connector: &api::ConnectorData, + ) -> RouterResult> { + let payment_method = &self + .payment_attempt + .payment_method + .get_required_value("PaymentMethod")?; + + let data = if *payment_method == enums::PaymentMethod::OpenBanking { + payments::get_merchant_bank_data_for_open_banking_connectors( + merchant_connector_account, + key_store, + connector, + state, + merchant_account, + ) + .await? + } else { + None + }; + + Ok(data) + } } #[async_trait] impl Feature for types::PaymentsAuthorizeRouterData { @@ -170,6 +203,14 @@ impl Feature for types::PaymentsAu authorize_preprocessing_steps(state, &self, true, connector).await } + async fn postprocessing_steps<'a>( + self, + state: &SessionState, + connector: &api::ConnectorData, + ) -> RouterResult { + authorize_postprocessing_steps(state, &self, true, connector).await + } + async fn create_connector_customer<'a>( &self, state: &SessionState, @@ -399,3 +440,53 @@ pub async fn authorize_preprocessing_steps( Ok(router_data.clone()) } } + +pub async fn authorize_postprocessing_steps( + state: &SessionState, + router_data: &types::RouterData, + confirm: bool, + connector: &api::ConnectorData, +) -> RouterResult> { + if confirm { + let connector_integration: services::BoxedPaymentConnectorIntegrationInterface< + api::PostProcessing, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, + > = connector.connector.get_connector_integration(); + + let postprocessing_request_data = + types::PaymentsPostProcessingData::try_from(router_data.to_owned())?; + + let postprocessing_response_data: Result< + types::PaymentsResponseData, + types::ErrorResponse, + > = Err(types::ErrorResponse::default()); + + let postprocessing_router_data = + helpers::router_data_type_conversion::<_, api::PostProcessing, _, _, _, _>( + router_data.clone(), + postprocessing_request_data, + postprocessing_response_data, + ); + + let resp = services::execute_connector_processing_step( + state, + connector_integration, + &postprocessing_router_data, + payments::CallConnectorAction::Trigger, + None, + ) + .await + .to_payment_failed_response()?; + + let authorize_router_data = helpers::router_data_type_conversion::<_, F, _, _, _, _>( + resp.clone(), + router_data.request.to_owned(), + resp.response, + ); + + Ok(authorize_router_data) + } else { + Ok(router_data.clone()) + } +} diff --git a/crates/router/src/core/payments/flows/cancel_flow.rs b/crates/router/src/core/payments/flows/cancel_flow.rs index d61730117a83..486e8bb75dcc 100644 --- a/crates/router/src/core/payments/flows/cancel_flow.rs +++ b/crates/router/src/core/payments/flows/cancel_flow.rs @@ -24,6 +24,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Void, @@ -36,9 +37,21 @@ impl ConstructFlowSpecificData( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/capture_flow.rs b/crates/router/src/core/payments/flows/capture_flow.rs index 1c4f76d9833b..a7f30f2a41b3 100644 --- a/crates/router/src/core/payments/flows/capture_flow.rs +++ b/crates/router/src/core/payments/flows/capture_flow.rs @@ -24,6 +24,7 @@ impl key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Capture, @@ -36,9 +37,21 @@ impl key_store, customer, merchant_connector_account, + merchant_recipient_data, )) .await } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/complete_authorize_flow.rs b/crates/router/src/core/payments/flows/complete_authorize_flow.rs index 6bef7e958595..d3ae67f46c34 100644 --- a/crates/router/src/core/payments/flows/complete_authorize_flow.rs +++ b/crates/router/src/core/payments/flows/complete_authorize_flow.rs @@ -28,6 +28,7 @@ impl key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult< types::RouterData< api::CompleteAuthorize, @@ -46,9 +47,21 @@ impl key_store, customer, merchant_connector_account, + merchant_recipient_data, )) .await } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs index 2aa808f95323..3bc05cedb457 100644 --- a/crates/router/src/core/payments/flows/incremental_authorization_flow.rs +++ b/crates/router/src/core/payments/flows/incremental_authorization_flow.rs @@ -27,6 +27,7 @@ impl key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::IncrementalAuthorization, @@ -39,9 +40,21 @@ impl key_store, customer, merchant_connector_account, + merchant_recipient_data, )) .await } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/psync_flow.rs b/crates/router/src/core/payments/flows/psync_flow.rs index d094fe6a21da..cb6c6f50d5ea 100644 --- a/crates/router/src/core/payments/flows/psync_flow.rs +++ b/crates/router/src/core/payments/flows/psync_flow.rs @@ -25,6 +25,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult< types::RouterData, > { @@ -39,9 +40,21 @@ impl ConstructFlowSpecificData( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/reject_flow.rs b/crates/router/src/core/payments/flows/reject_flow.rs index cb1dcd5b19f3..501c0a40dab3 100644 --- a/crates/router/src/core/payments/flows/reject_flow.rs +++ b/crates/router/src/core/payments/flows/reject_flow.rs @@ -23,6 +23,7 @@ impl ConstructFlowSpecificData, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Reject, @@ -35,9 +36,21 @@ impl ConstructFlowSpecificData( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 664038454a49..883203ad99ed 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -39,6 +39,7 @@ impl key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::Session, @@ -51,9 +52,21 @@ impl key_store, customer, merchant_connector_account, + merchant_recipient_data, )) .await } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &routes::SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/flows/setup_mandate_flow.rs b/crates/router/src/core/payments/flows/setup_mandate_flow.rs index 773677f3e7cf..0d4648b81f46 100644 --- a/crates/router/src/core/payments/flows/setup_mandate_flow.rs +++ b/crates/router/src/core/payments/flows/setup_mandate_flow.rs @@ -31,6 +31,7 @@ impl key_store: &domain::MerchantKeyStore, customer: &Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult { Box::pin(transformers::construct_payment_router_data::< api::SetupMandate, @@ -43,9 +44,21 @@ impl key_store, customer, merchant_connector_account, + merchant_recipient_data, )) .await } + + async fn get_merchant_recipient_data<'a>( + &self, + _state: &SessionState, + _merchant_account: &domain::MerchantAccount, + _key_store: &domain::MerchantKeyStore, + _merchant_connector_account: &helpers::MerchantConnectorAccountType, + _connector: &api::ConnectorData, + ) -> RouterResult> { + Ok(None) + } } #[async_trait] diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index da44703732f7..1579322995b5 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -66,8 +66,9 @@ use crate::{ self, enums as storage_enums, ephemeral_key, CardTokenData, CustomerUpdate::Update, }, transformers::{ForeignFrom, ForeignTryFrom}, - AdditionalPaymentMethodConnectorResponse, ErrorResponse, MandateReference, - PaymentsResponseData, RecurringMandatePaymentData, RouterData, + AdditionalMerchantData, AdditionalPaymentMethodConnectorResponse, ErrorResponse, + MandateReference, MerchantAccountData, MerchantRecipientData, PaymentsResponseData, + RecipientIdType, RecurringMandatePaymentData, RouterData, }, utils::{ self, @@ -2429,6 +2430,7 @@ pub fn validate_payment_method_type_against_payment_method( | api_enums::PaymentMethodType::Bizum | api_enums::PaymentMethodType::Interac | api_enums::PaymentMethodType::OpenBankingUk + | api_enums::PaymentMethodType::OpenBankingPIS ), api_enums::PaymentMethod::BankTransfer => matches!( payment_method_type, @@ -2503,6 +2505,10 @@ pub fn validate_payment_method_type_against_payment_method( | api_enums::PaymentMethodType::MomoAtm | api_enums::PaymentMethodType::CardRedirect ), + api_enums::PaymentMethod::OpenBanking => matches!( + payment_method_type, + api_enums::PaymentMethodType::OpenBankingPIS + ), } } @@ -3341,6 +3347,15 @@ impl MerchantConnectorAccountType { Self::CacheVal(_) => None, } } + + pub fn get_additional_merchant_data( + &self, + ) -> Option>> { + match self { + Self::DbVal(db_val) => db_val.additional_merchant_data.clone(), + Self::CacheVal(_) => None, + } + } } /// Query for merchant connector account either by business label or profile id @@ -4024,6 +4039,9 @@ pub async fn get_additional_payment_data( api_models::payments::PaymentMethodData::CardToken(_) => { api_models::payments::AdditionalPaymentData::CardToken {} } + api_models::payments::PaymentMethodData::OpenBanking(_) => { + api_models::payments::AdditionalPaymentData::OpenBanking {} + } } } @@ -4572,6 +4590,11 @@ pub fn get_key_params_for_surcharge_details( gift_card.get_payment_method_type(), None, )), + api_models::payments::PaymentMethodData::OpenBanking(ob_data) => Some(( + common_enums::PaymentMethod::OpenBanking, + ob_data.get_payment_method_type(), + None, + )), api_models::payments::PaymentMethodData::CardToken(_) => None, } } @@ -4669,6 +4692,40 @@ pub fn validate_session_expiry(session_expiry: u32) -> Result<(), errors::ApiErr } } +pub fn get_recipient_id_for_open_banking( + merchant_data: &AdditionalMerchantData, +) -> Result, errors::ApiErrorResponse> { + match merchant_data { + AdditionalMerchantData::OpenBankingRecipientData(data) => match data { + MerchantRecipientData::ConnectorRecipientId(id) => Ok(Some(id.peek().clone())), + MerchantRecipientData::AccountData(acc_data) => match acc_data { + MerchantAccountData::Bacs { + connector_recipient_id, + .. + } => match connector_recipient_id { + Some(RecipientIdType::ConnectorId(id)) => Ok(Some(id.peek().clone())), + Some(RecipientIdType::LockerId(id)) => Ok(Some(id.peek().clone())), + _ => Err(errors::ApiErrorResponse::InvalidConnectorConfiguration { + config: "recipient_id".to_string(), + }), + }, + MerchantAccountData::Iban { + connector_recipient_id, + .. + } => match connector_recipient_id { + Some(RecipientIdType::ConnectorId(id)) => Ok(Some(id.peek().clone())), + Some(RecipientIdType::LockerId(id)) => Ok(Some(id.peek().clone())), + _ => Err(errors::ApiErrorResponse::InvalidConnectorConfiguration { + config: "recipient_id".to_string(), + }), + }, + }, + _ => Err(errors::ApiErrorResponse::InvalidConnectorConfiguration { + config: "recipient_id".to_string(), + }), + }, + } +} // This function validates the intent fulfillment time expiry set by the merchant in the request pub fn validate_intent_fulfillment_expiry( intent_fulfillment_time: u32, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index a28b9fe3dd2d..fc075f29799b 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -1116,6 +1116,7 @@ async fn payment_response_update_tracker( types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. } => { (None, None) } + types::PaymentsResponseData::PostProcessingResponse { .. } => (None, None), types::PaymentsResponseData::IncrementalAuthorizationResponse { .. } => (None, None), @@ -1338,13 +1339,14 @@ async fn payment_response_update_tracker( .in_current_span(), ); - let (payment_intent, _, _) = futures::try_join!( + let (payment_intent, _, payment_attempt) = futures::try_join!( utils::flatten_join_error(payment_intent_fut), utils::flatten_join_error(mandate_update_fut), utils::flatten_join_error(payment_attempt_fut) )?; payment_data.payment_intent = payment_intent; + payment_data.payment_attempt = payment_attempt; router_data.payment_method_status.and_then(|status| { payment_data .payment_method_info diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index 6160ffacb0a6..bf5ae5aa6350 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -306,7 +306,7 @@ where ) .await?; - payments::call_connector_service( + let (router_data, _mca) = payments::call_connector_service( state, req_state, merchant_account, @@ -323,7 +323,9 @@ where business_profile, true, ) - .await + .await?; + + Ok(router_data) } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs index 9b21fddf4f04..7da5ec132ac7 100644 --- a/crates/router/src/core/payments/transformers.rs +++ b/crates/router/src/core/payments/transformers.rs @@ -38,6 +38,7 @@ use crate::{ }; #[instrument(skip_all)] +#[allow(clippy::too_many_arguments)] pub async fn construct_payment_router_data<'a, F, T>( state: &'a SessionState, payment_data: PaymentData, @@ -46,6 +47,7 @@ pub async fn construct_payment_router_data<'a, F, T>( _key_store: &domain::MerchantKeyStore, customer: &'a Option, merchant_connector_account: &helpers::MerchantConnectorAccountType, + merchant_recipient_data: Option, ) -> RouterResult> where T: TryFrom>, @@ -157,7 +159,14 @@ where .payment_attempt .authentication_type .unwrap_or_default(), - connector_meta_data: merchant_connector_account.get_metadata(), + connector_meta_data: if let Some(data) = merchant_recipient_data { + let val = serde_json::to_value(data) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encoding MerchantRecipientData")?; + Some(Secret::new(val)) + } else { + merchant_connector_account.get_metadata() + }, connector_wallets_details: merchant_connector_account.get_connector_wallets_details(), request: T::try_from(additional_data)?, response, @@ -909,7 +918,7 @@ where { // If the operation is confirm, we will send session token response in next action if format!("{operation:?}").eq("PaymentConfirm") { - payment_attempt + let condition1 = payment_attempt .connector .as_ref() .map(|connector| { @@ -924,7 +933,36 @@ where Some(false) } }) - .unwrap_or(false) + .unwrap_or(false); + + // This condition to be triggered for open banking connectors, third party SDK session token will be provided + let condition2 = payment_attempt + .connector + .as_ref() + .map(|connector| matches!(connector.as_str(), "plaid")) + .and_then(|is_connector_supports_third_party_sdk| { + if is_connector_supports_third_party_sdk { + payment_attempt + .payment_method + .map(|pm| matches!(pm, diesel_models::enums::PaymentMethod::OpenBanking)) + .and_then(|first_match| { + payment_attempt + .payment_method_type + .map(|pmt| { + matches!( + pmt, + diesel_models::enums::PaymentMethodType::OpenBankingPIS + ) + }) + .map(|second_match| first_match && second_match) + }) + } else { + Some(false) + } + }) + .unwrap_or(false); + + condition1 || condition2 } else { false } @@ -1297,6 +1335,11 @@ impl TryFrom> for types::PaymentsAuthoriz .map(|customer| customer.clone().into_inner()) }); + let customer_id = additional_data + .customer_data + .as_ref() + .map(|data| data.customer_id.clone()); + let charges = match payment_data.payment_intent.charges { Some(charges) => charges .peek() @@ -1340,7 +1383,7 @@ impl TryFrom> for types::PaymentsAuthoriz router_return_url, webhook_url, complete_authorize_url, - customer_id: None, + customer_id, surcharge_details: payment_data.surcharge_details, request_incremental_authorization: matches!( payment_data diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 0d4c525f1047..e91a55e860c8 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -329,6 +329,7 @@ pub async fn filter_payout_methods( | common_enums::PaymentMethod::RealTimePayment | common_enums::PaymentMethod::Upi | common_enums::PaymentMethod::Voucher + | common_enums::PaymentMethod::OpenBanking | common_enums::PaymentMethod::GiftCard => continue, } } diff --git a/crates/router/src/types.rs b/crates/router/src/types.rs index b5f97a09f703..c5e536de3aa8 100644 --- a/crates/router/src/types.rs +++ b/crates/router/src/types.rs @@ -42,9 +42,10 @@ pub use hyperswitch_domain_models::{ DestinationChargeRefund, DirectChargeRefund, MandateRevokeRequestData, MultipleCaptureRequestData, PaymentMethodTokenizationData, PaymentsApproveData, PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, - PaymentsIncrementalAuthorizationData, PaymentsPreProcessingData, PaymentsRejectData, - PaymentsSessionData, PaymentsSyncData, RefundsData, ResponseId, RetrieveFileRequestData, - SetupMandateRequestData, SubmitEvidenceRequestData, SyncRequestType, UploadFileRequestData, + PaymentsIncrementalAuthorizationData, PaymentsPostProcessingData, + PaymentsPreProcessingData, PaymentsRejectData, PaymentsSessionData, PaymentsSyncData, + RefundsData, ResponseId, RetrieveFileRequestData, SetupMandateRequestData, + SubmitEvidenceRequestData, SyncRequestType, UploadFileRequestData, VerifyWebhookSourceRequestData, }, router_response_types::{ @@ -80,6 +81,8 @@ pub type PaymentsAuthorizeRouterData = RouterData; pub type PaymentsPreProcessingRouterData = RouterData; +pub type PaymentsPostProcessingRouterData = + RouterData; pub type PaymentsAuthorizeSessionTokenRouterData = RouterData; pub type PaymentsCompleteAuthorizeRouterData = @@ -163,6 +166,11 @@ pub type PaymentsPreProcessingType = dyn services::ConnectorIntegration< PaymentsPreProcessingData, PaymentsResponseData, >; +pub type PaymentsPostProcessingType = dyn services::ConnectorIntegration< + api::PostProcessing, + PaymentsPostProcessingData, + PaymentsResponseData, +>; pub type PaymentsCompleteAuthorizeType = dyn services::ConnectorIntegration< api::CompleteAuthorize, CompleteAuthorizeData, diff --git a/crates/router/src/types/api/payments.rs b/crates/router/src/types/api/payments.rs index e1dbf452eff9..3c1d8175e8bc 100644 --- a/crates/router/src/types/api/payments.rs +++ b/crates/router/src/types/api/payments.rs @@ -2,10 +2,10 @@ pub use api_models::payments::{ AcceptanceType, Address, AddressDetails, Amount, AuthenticationForStartResponse, Card, CryptoData, CustomerAcceptance, HeaderPayload, MandateAmountData, MandateData, MandateTransactionType, MandateType, MandateValidationFields, NextActionType, OnlineMandate, - PayLaterData, PaymentIdType, PaymentListConstraints, PaymentListFilterConstraints, - PaymentListFilters, PaymentListFiltersV2, PaymentListResponse, PaymentListResponseV2, - PaymentMethodData, PaymentMethodDataRequest, PaymentMethodDataResponse, PaymentOp, - PaymentRetrieveBody, PaymentRetrieveBodyWithCredentials, PaymentsApproveRequest, + OpenBankingSessionToken, PayLaterData, PaymentIdType, PaymentListConstraints, + PaymentListFilterConstraints, PaymentListFilters, PaymentListFiltersV2, PaymentListResponse, + PaymentListResponseV2, PaymentMethodData, PaymentMethodDataRequest, PaymentMethodDataResponse, + PaymentOp, PaymentRetrieveBody, PaymentRetrieveBodyWithCredentials, PaymentsApproveRequest, PaymentsCancelRequest, PaymentsCaptureRequest, PaymentsCompleteAuthorizeRequest, PaymentsExternalAuthenticationRequest, PaymentsIncrementalAuthorizationRequest, PaymentsManualUpdateRequest, PaymentsRedirectRequest, PaymentsRedirectionResponse, @@ -18,14 +18,14 @@ use error_stack::ResultExt; pub use hyperswitch_domain_models::router_flow_types::payments::{ Approve, Authorize, AuthorizeSessionToken, Balance, Capture, CompleteAuthorize, CreateConnectorCustomer, IncrementalAuthorization, InitPayment, PSync, PaymentMethodToken, - PreProcessing, Reject, Session, SetupMandate, Void, + PostProcessing, PreProcessing, Reject, Session, SetupMandate, Void, }; pub use super::payments_v2::{ ConnectorCustomerV2, MandateSetupV2, PaymentApproveV2, PaymentAuthorizeSessionTokenV2, PaymentAuthorizeV2, PaymentCaptureV2, PaymentIncrementalAuthorizationV2, PaymentRejectV2, PaymentSessionV2, PaymentSyncV2, PaymentTokenV2, PaymentV2, PaymentVoidV2, - PaymentsCompleteAuthorizeV2, PaymentsPreProcessingV2, + PaymentsCompleteAuthorizeV2, PaymentsPostProcessingV2, PaymentsPreProcessingV2, }; use crate::{ core::errors, @@ -172,6 +172,15 @@ pub trait PaymentsPreProcessing: { } +pub trait PaymentsPostProcessing: + api::ConnectorIntegration< + PostProcessing, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, +> +{ +} + pub trait Payment: api_types::ConnectorCommon + api_types::ConnectorValidation @@ -187,6 +196,7 @@ pub trait Payment: + PaymentSession + PaymentToken + PaymentsPreProcessing + + PaymentsPostProcessing + ConnectorCustomer + PaymentIncrementalAuthorization { diff --git a/crates/router/src/types/api/payments_v2.rs b/crates/router/src/types/api/payments_v2.rs index fc490ea3d790..639dc0de5bcc 100644 --- a/crates/router/src/types/api/payments_v2.rs +++ b/crates/router/src/types/api/payments_v2.rs @@ -3,7 +3,7 @@ use hyperswitch_domain_models::{ router_flow_types::payments::{ Approve, Authorize, AuthorizeSessionToken, Capture, CompleteAuthorize, CreateConnectorCustomer, IncrementalAuthorization, PSync, PaymentMethodToken, - PreProcessing, Reject, Session, SetupMandate, Void, + PostProcessing, PreProcessing, Reject, Session, SetupMandate, Void, }, }; @@ -152,6 +152,16 @@ pub trait PaymentsPreProcessingV2: { } +pub trait PaymentsPostProcessingV2: + api::ConnectorIntegrationV2< + PostProcessing, + PaymentFlowData, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, +> +{ +} + pub trait PaymentV2: api_types::ConnectorCommon + api_types::ConnectorValidation @@ -167,6 +177,7 @@ pub trait PaymentV2: + PaymentSessionV2 + PaymentTokenV2 + PaymentsPreProcessingV2 + + PaymentsPostProcessingV2 + ConnectorCustomerV2 + PaymentIncrementalAuthorizationV2 { diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 78c2bf231f2e..9a42916f68d3 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -478,6 +478,7 @@ impl ForeignFrom for api_enums::PaymentMethod { | api_enums::PaymentMethodType::OnlineBankingPoland | api_enums::PaymentMethodType::OnlineBankingSlovakia | api_enums::PaymentMethodType::OpenBankingUk + | api_enums::PaymentMethodType::OpenBankingPIS | api_enums::PaymentMethodType::Przelewy24 | api_enums::PaymentMethodType::Trustly | api_enums::PaymentMethodType::Bizum @@ -556,6 +557,7 @@ impl ForeignTryFrom for api_enums::PaymentMethod { payments::PaymentMethodData::Voucher(..) => Ok(Self::Voucher), payments::PaymentMethodData::GiftCard(..) => Ok(Self::GiftCard), payments::PaymentMethodData::CardRedirect(..) => Ok(Self::CardRedirect), + payments::PaymentMethodData::OpenBanking(..) => Ok(Self::OpenBanking), payments::PaymentMethodData::MandatePayment => { Err(errors::ApiErrorResponse::InvalidRequestData { message: ("Mandate payments cannot have payment_method_data field".to_string()), diff --git a/crates/router/tests/connectors/cashtocode.rs b/crates/router/tests/connectors/cashtocode.rs index 3e2708fb1f43..3af6e277de9e 100644 --- a/crates/router/tests/connectors/cashtocode.rs +++ b/crates/router/tests/connectors/cashtocode.rs @@ -1,4 +1,5 @@ use api_models::payments::{Address, AddressDetails}; +use common_utils::id_type; use router::types::{self, domain, storage::enums}; use crate::{ @@ -41,6 +42,7 @@ impl CashtocodeTest { payment_method_type: Option, payment_method_data: domain::PaymentMethodData, ) -> Option { + let cust_id = id_type::CustomerId::from("John Doe".into()); Some(types::PaymentsAuthorizeData { amount: 1000, currency: enums::Currency::EUR, @@ -66,7 +68,7 @@ impl CashtocodeTest { router_return_url: Some(String::from("https://google.com")), webhook_url: None, complete_authorize_url: None, - customer_id: Some("John Doe".to_owned()), + customer_id: if let Ok(id) = cust_id { Some(id) } else { None }, surcharge_details: None, request_incremental_authorization: false, metadata: None, diff --git a/crates/router/tests/connectors/utils.rs b/crates/router/tests/connectors/utils.rs index b03fee739f70..736c9ae6b536 100644 --- a/crates/router/tests/connectors/utils.rs +++ b/crates/router/tests/connectors/utils.rs @@ -560,6 +560,7 @@ pub trait ConnectorActions: Connector { Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. }) => None, Ok(types::PaymentsResponseData::MultipleCaptureResponse { .. }) => None, Ok(types::PaymentsResponseData::IncrementalAuthorizationResponse { .. }) => None, + Ok(types::PaymentsResponseData::PostProcessingResponse { .. }) => None, Err(_) => None, } } @@ -1069,6 +1070,7 @@ pub fn get_connector_transaction_id( Ok(types::PaymentsResponseData::ThreeDSEnrollmentResponse { .. }) => None, Ok(types::PaymentsResponseData::MultipleCaptureResponse { .. }) => None, Ok(types::PaymentsResponseData::IncrementalAuthorizationResponse { .. }) => None, + Ok(types::PaymentsResponseData::PostProcessingResponse { .. }) => None, Err(_) => None, } } From 96edf52ca639178e01dc4c3e008611b847bc358f Mon Sep 17 00:00:00 2001 From: Pa1NarK <69745008+pixincreate@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:46:10 +0530 Subject: [PATCH 04/26] refactor(cypress): nullify false positives (#5303) --- .../cypress/e2e/PaymentUtils/Adyen.js | 2 +- .../cypress/e2e/PaymentUtils/BankOfAmerica.js | 2 +- .../cypress/e2e/PaymentUtils/Bluesnap.js | 2 +- .../cypress/e2e/PaymentUtils/Cybersource.js | 2 +- cypress-tests/cypress/e2e/PaymentUtils/Nmi.js | 38 +--- .../cypress/e2e/PaymentUtils/Paypal.js | 2 +- .../cypress/e2e/PaymentUtils/Stripe.js | 2 +- .../cypress/e2e/PaymentUtils/Trustpay.js | 2 +- cypress-tests/cypress/support/commands.js | 181 +++++++++++------- 9 files changed, 119 insertions(+), 114 deletions(-) diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js b/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js index 8513f596f4fe..1e2e7e0f3c3b 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Adyen.js @@ -96,7 +96,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "succeeded", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js b/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js index 79ea91e1ed50..544a0adec9bb 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/BankOfAmerica.js @@ -94,7 +94,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "succeeded", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js b/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js index 6cfc84de4369..7a7e156b0707 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Bluesnap.js @@ -62,7 +62,7 @@ export const connectorDetails = { status: 200, trigger_skip: true, body: { - status: "succeeded", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js b/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js index 79ea91e1ed50..544a0adec9bb 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Cybersource.js @@ -94,7 +94,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "succeeded", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js b/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js index 70eea459dcc0..a5440c4ec880 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Nmi.js @@ -14,40 +14,6 @@ const successfulThreeDSTestCardDetails = { card_cvc: "999", }; -const singleUseMandateData = { - customer_acceptance: { - acceptance_type: "offline", - accepted_at: "1963-05-03T04:07:52.723Z", - online: { - ip_address: "125.0.0.1", - user_agent: "amet irure esse", - }, - }, - mandate_type: { - single_use: { - amount: 8000, - currency: "USD", - }, - }, -}; - -const multiUseMandateData = { - customer_acceptance: { - acceptance_type: "offline", - accepted_at: "1963-05-03T04:07:52.723Z", - online: { - ip_address: "125.0.0.1", - user_agent: "amet irure esse", - }, - }, - mandate_type: { - multi_use: { - amount: 8000, - currency: "USD", - }, - }, -}; - export const connectorDetails = { card_pm: { PaymentIntent: { @@ -78,7 +44,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "processing", + status: "requires_customer_action", }, }, }, @@ -94,7 +60,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "processing", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js b/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js index 14e69feb3f4a..256606c29c9d 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Paypal.js @@ -64,7 +64,7 @@ export const connectorDetails = { status: 200, trigger_skip: true, body: { - status: "succeeded", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js b/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js index c2f8b171dd56..f2cf89df243c 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Stripe.js @@ -96,7 +96,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "succeeded", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js b/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js index 651bc7d06b3d..dae0b3a5deea 100644 --- a/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js +++ b/cypress-tests/cypress/e2e/PaymentUtils/Trustpay.js @@ -63,7 +63,7 @@ export const connectorDetails = { Response: { status: 200, body: { - status: "succeeded", + status: "requires_customer_action", }, }, }, diff --git a/cypress-tests/cypress/support/commands.js b/cypress-tests/cypress/support/commands.js index af7600890b66..5aa98a6e4a84 100644 --- a/cypress-tests/cypress/support/commands.js +++ b/cypress-tests/cypress/support/commands.js @@ -121,6 +121,10 @@ Cypress.Commands.add( "cli_log", "response status -> " + JSON.stringify(response.status) ); + + throw new Error( + `Connector Create Call Failed ${response.body.error.message}` + ); } }); } @@ -174,6 +178,10 @@ Cypress.Commands.add( "cli_log", "response status -> " + JSON.stringify(response.status) ); + + throw new Error( + `Connector Create Call Failed ${response.body.error.message}` + ); } }); } @@ -237,6 +245,10 @@ Cypress.Commands.add( "cli_log", "response status -> " + JSON.stringify(response.status) ); + + throw new Error( + `Connector Create Call Failed ${response.body.error.message}` + ); } }); } @@ -442,7 +454,7 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { expect(response.body).to.have.property("payment_method_id"); expect(response.body).to.have.property("client_secret"); globalState.set("paymentMethodId", response.body.payment_method_id); @@ -474,7 +486,7 @@ Cypress.Commands.add( }).then((response) => { logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { globalState.set("paymentID", paymentIntentID); if (response.body.capture_method === "automatic") { if (response.body.authentication_type === "three_ds") { @@ -485,12 +497,17 @@ Cypress.Commands.add( "nextActionUrl", response.body.next_action.redirect_to_url ); + for (const key in res_data.body) { + expect(res_data.body[key]).to.equal(response.body[key]); + } } else if (response.body.authentication_type === "no_three_ds") { for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid authentication type ${response.body.authentication_type}` + ); } } else if (response.body.capture_method === "manual") { if (response.body.authentication_type === "three_ds") { @@ -506,8 +523,14 @@ Cypress.Commands.add( expect(res_data.body[key]).to.equal(response.body[key]); } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid authentication type ${response.body.authentication_type}` + ); } + } else { + throw new Error( + `Invalid capture method ${response.body.capture_method}` + ); } } else { defaultErrorHandler(response, res_data); @@ -538,7 +561,7 @@ Cypress.Commands.add( body: confirmBody, }).then((response) => { logRequestId(response.headers["x-request-id"]); - if (res_data.status === 200) { + if (response.status === 200) { expect(response.headers["content-type"]).to.include("application/json"); globalState.set("paymentID", paymentIntentId); globalState.set("paymentMethodType", confirmBody.payment_method_type); @@ -574,7 +597,9 @@ Cypress.Commands.add( ); } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid capture method ${response.body.capture_method}` + ); } break; case "no_three_ds": @@ -590,11 +615,15 @@ Cypress.Commands.add( response.body.next_action.redirect_to_url ); } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid capture method ${response.body.capture_method}` + ); } break; default: - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid authentication type ${response.body.authentication_type}` + ); } } else { defaultErrorHandler(response, res_data); @@ -626,7 +655,7 @@ Cypress.Commands.add( }).then((response) => { logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { globalState.set("paymentID", paymentIntentID); if ( response.body.capture_method === "automatic" || @@ -653,7 +682,9 @@ Cypress.Commands.add( break; } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid capture method ${response.body.capture_method}` + ); } } else { defaultErrorHandler(response, res_data); @@ -685,7 +716,7 @@ Cypress.Commands.add( }).then((response) => { logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { if ( response.body.capture_method === "automatic" || response.body.capture_method === "manual" @@ -708,7 +739,9 @@ Cypress.Commands.add( ); } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid capture method ${response.body.capture_method}` + ); } } else { defaultErrorHandler(response, res_data); @@ -745,7 +778,7 @@ Cypress.Commands.add( }).then((response) => { logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { if (response.body.capture_method === "automatic") { expect(response.body).to.have.property("status"); globalState.set("paymentAmount", createConfirmPaymentBody.amount); @@ -763,7 +796,9 @@ Cypress.Commands.add( expect(res_data.body[key]).to.equal(response.body[key]); } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid authentication type: ${response.body.authentication_type}` + ); } } else if (response.body.capture_method === "manual") { expect(response.body).to.have.property("status"); @@ -782,7 +817,9 @@ Cypress.Commands.add( expect(res_data.body[key]).to.equal(response.body[key]); } } else { - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid authentication type: ${response.body.authentication_type}` + ); } } } else { @@ -813,7 +850,7 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { globalState.set("paymentID", paymentIntentID); if (response.body.capture_method === "automatic") { if (response.body.authentication_type === "three_ds") { @@ -830,7 +867,9 @@ Cypress.Commands.add( ); } else { // Handle other authentication types as needed - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid authentication type: ${response.body.authentication_type}` + ); } } else if (response.body.capture_method === "manual") { if (response.body.authentication_type === "three_ds") { @@ -846,8 +885,15 @@ Cypress.Commands.add( ); } else { // Handle other authentication types as needed - defaultErrorHandler(response, res_data); + throw new Error( + `Invalid authentication type: ${response.body.authentication_type}` + ); } + } else { + // Handle other capture methods as needed + throw new Error( + `Invalid capture method: ${response.body.capture_method}` + ); } } else { defaultErrorHandler(response, res_data); @@ -903,7 +949,7 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } @@ -953,7 +999,7 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { globalState.set("refundId", response.body.refund_id); for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); @@ -1022,10 +1068,9 @@ Cypress.Commands.add( }).then((response) => { logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { globalState.set("paymentID", response.body.payment_id); - globalState.set("paymentID", response.body.payment_id); if (requestBody.mandate_data === null) { expect(response.body).to.have.property("payment_method_id"); globalState.set("paymentMethodId", response.body.payment_method_id); @@ -1033,6 +1078,7 @@ Cypress.Commands.add( expect(response.body).to.have.property("mandate_id"); globalState.set("mandateId", response.body.mandate_id); } + if (response.body.capture_method === "automatic") { expect(response.body).to.have.property("mandate_id"); if (response.body.authentication_type === "three_ds") { @@ -1044,28 +1090,46 @@ Cypress.Commands.add( "nextActionUrl", response.body.next_action.redirect_to_url ); - cy.log(response.body); cy.log(nextActionUrl); + for (const key in res_data.body) { + expect(res_data.body[key]).to.equal(response.body[key]); + } } else if (response.body.authentication_type === "no_three_ds") { for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } - } - for (const key in res_data.body) { - expect(res_data.body[key]).to.equal(response.body[key]); + } else { + throw new Error( + `Invalid authentication type ${response.body.authentication_type}` + ); } } else if (response.body.capture_method === "manual") { if (response.body.authentication_type === "three_ds") { - expect(response.body).to.have.property("next_action"); + expect(response.body) + .to.have.property("next_action") + .to.have.property("redirect_to_url"); const nextActionUrl = response.body.next_action.redirect_to_url; globalState.set( "nextActionUrl", response.body.next_action.redirect_to_url ); + cy.log(nextActionUrl); + for (const key in res_data.body) { + expect(res_data.body[key]).to.equal(response.body[key]); + } + } else if (response.body.authentication_type === "no_three_ds") { + for (const key in res_data.body) { + expect(res_data.body[key]).to.equal(response.body[key]); + } + } else { + throw new Error( + `Invalid authentication type ${response.body.authentication_type}` + ); } - for (const key in res_data.body) { - expect(res_data.body[key]).to.equal(response.body[key]); - } + } else { + throw new Error( + `Invalid capture method ${response.body.capture_method}` + ); } } else { defaultErrorHandler(response, res_data); @@ -1102,7 +1166,6 @@ Cypress.Commands.add( .to.have.property("next_action") .to.have.property("redirect_to_url"); const nextActionUrl = response.body.next_action.redirect_to_url; - cy.log(response.body); cy.log(nextActionUrl); } else if (response.body.authentication_type === "no_three_ds") { expect(response.body.status).to.equal("succeeded"); @@ -1360,17 +1423,14 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { globalState.set("payoutAmount", createConfirmPayoutBody.amount); globalState.set("payoutID", response.body.payout_id); for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); } @@ -1407,17 +1467,14 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { globalState.set("payoutAmount", createConfirmPayoutBody.amount); globalState.set("payoutID", response.body.payout_id); for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); } @@ -1441,15 +1498,12 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); } @@ -1474,15 +1528,12 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); } @@ -1525,7 +1576,7 @@ Cypress.Commands.add("createJWTToken", (req_data, res_data, globalState) => { logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { expect(response.body).to.have.property("token"); //set jwt_token globalState.set("jwtToken", response.body.token); @@ -1543,10 +1594,7 @@ Cypress.Commands.add("createJWTToken", (req_data, res_data, globalState) => { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); }); @@ -1596,17 +1644,14 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { expect(response.body).to.have.property("id"); globalState.set("routingConfigId", response.body.id); for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); } @@ -1629,16 +1674,13 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { expect(response.body.id).to.equal(routing_config_id); for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); } @@ -1661,16 +1703,13 @@ Cypress.Commands.add( logRequestId(response.headers["x-request-id"]); expect(response.headers["content-type"]).to.include("application/json"); - if (res_data.status === 200) { + if (response.status === 200) { expect(response.body.id).to.equal(routing_config_id); for (const key in res_data.body) { expect(res_data.body[key]).to.equal(response.body[key]); } } else { - expect(response.body).to.have.property("error"); - for (const key in res_data.body.error) { - expect(res_data.body.error[key]).to.equal(response.body.error[key]); - } + defaultErrorHandler(response, res_data); } }); } From 35c9b8afe1a09b858c79c0ce13cf5c24d200d3fd Mon Sep 17 00:00:00 2001 From: Sandeep Kumar <83278309+tsdk02@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:56:41 +0530 Subject: [PATCH 05/26] feat(globalsearch): Added search_tags based filter for global search in dashboard (#5341) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Abhishek Kanojia <89402434+Abhitator216@users.noreply.github.com> --- api-reference/openapi_spec.json | 10 +++---- crates/analytics/src/search.rs | 36 +++++++++++++++++++++++ crates/api_models/src/analytics/search.rs | 2 ++ crates/api_models/src/payments.rs | 8 +++-- crates/masking/src/strategy.rs | 2 ++ 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 79605b77fa4b..12b3d2a05fd7 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -9515,11 +9515,11 @@ "nullable": true }, "search_tags": { - "allOf": [ - { - "$ref": "#/components/schemas/RedirectResponse" - } - ], + "type": "array", + "items": { + "type": "string" + }, + "description": "Additional tags to be used for global search", "nullable": true } } diff --git a/crates/analytics/src/search.rs b/crates/analytics/src/search.rs index 269864bf44ac..a5bd117bd63a 100644 --- a/crates/analytics/src/search.rs +++ b/crates/analytics/src/search.rs @@ -66,6 +66,24 @@ pub async fn msearch_results( .switch()?; } }; + if let Some(search_tags) = filters.search_tags { + if !search_tags.is_empty() { + query_builder + .add_filter_clause( + "feature_metadata.search_tags.keyword".to_string(), + search_tags + .iter() + .filter_map(|search_tag| { + // TODO: Add trait based inputs instead of converting this to strings + serde_json::to_value(search_tag) + .ok() + .and_then(|a| a.as_str().map(|a| a.to_string())) + }) + .collect(), + ) + .switch()?; + } + }; }; let response_text: OpenMsearchOutput = client @@ -173,6 +191,24 @@ pub async fn search_results( .switch()?; } }; + if let Some(search_tags) = filters.search_tags { + if !search_tags.is_empty() { + query_builder + .add_filter_clause( + "feature_metadata.search_tags.keyword".to_string(), + search_tags + .iter() + .filter_map(|search_tag| { + // TODO: Add trait based inputs instead of converting this to strings + serde_json::to_value(search_tag) + .ok() + .and_then(|a| a.as_str().map(|a| a.to_string())) + }) + .collect(), + ) + .switch()?; + } + }; }; query_builder .set_offset_n_count(search_req.offset, search_req.count) diff --git a/crates/api_models/src/analytics/search.rs b/crates/api_models/src/analytics/search.rs index 034a2a943563..d1af00de5692 100644 --- a/crates/api_models/src/analytics/search.rs +++ b/crates/api_models/src/analytics/search.rs @@ -1,4 +1,5 @@ use common_utils::hashing::HashedString; +use masking::WithType; use serde_json::Value; #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] @@ -7,6 +8,7 @@ pub struct SearchFilters { pub currency: Option>, pub status: Option>, pub customer_email: Option>>, + pub search_tags: Option>>, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index f53507fc13a1..c04af2044931 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -9,11 +9,12 @@ use common_utils::{ consts::default_payments_list_limit, crypto, ext_traits::{ConfigExt, Encode}, + hashing::HashedString, id_type, pii::{self, Email}, types::{MinorUnit, StringMajorUnit}, }; -use masking::{PeekInterface, Secret}; +use masking::{PeekInterface, Secret, WithType}; use router_derive::Setter; use serde::{ de::{self, Unexpected, Visitor}, @@ -4987,11 +4988,12 @@ pub struct PaymentsStartRequest { #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)] pub struct FeatureMetadata { /// Redirection response coming in request as metadata field only for redirection scenarios + #[schema(value_type = Option)] pub redirect_response: Option, // TODO: Convert this to hashedstrings to avoid PII sensitive data /// Additional tags to be used for global search - #[schema(value_type = Option)] - pub search_tags: Option>>, + #[schema(value_type = Option>)] + pub search_tags: Option>>, } ///frm message is an object sent inside the payments response...when frm is invoked, its value is Some(...), else its None diff --git a/crates/masking/src/strategy.rs b/crates/masking/src/strategy.rs index 8b4d9b0ec34d..eb705ca490a7 100644 --- a/crates/masking/src/strategy.rs +++ b/crates/masking/src/strategy.rs @@ -7,6 +7,8 @@ pub trait Strategy { } /// Debug with type +#[cfg_attr(feature = "serde", derive(serde::Deserialize))] +#[derive(Debug, Copy, Clone)] pub enum WithType {} impl Strategy for WithType { From 2d204c9f7348c4ed121ab472ef1b5bb8d9d32d24 Mon Sep 17 00:00:00 2001 From: Kashif Date: Wed, 17 Jul 2024 18:23:55 +0530 Subject: [PATCH 06/26] feat(payout_link): secure payout links using server side validations and client side headers (#5219) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- Cargo.lock | 1 + api-reference/openapi_spec.json | 11 + crates/api_models/src/admin.rs | 30 +- crates/common_utils/src/consts.rs | 16 + crates/common_utils/src/link_utils.rs | 146 ++++++++- crates/hyperswitch_domain_models/src/api.rs | 14 +- .../src/errors/api_error_response.rs | 5 + crates/router/Cargo.toml | 1 + .../router/src/compatibility/stripe/errors.rs | 8 +- crates/router/src/compatibility/wrap.rs | 13 +- crates/router/src/core/admin.rs | 24 +- .../payout_link/initiate/script.js | 292 ++++++++++-------- .../payout_link/status/styles.css | 30 +- crates/router/src/core/payment_methods.rs | 25 +- crates/router/src/core/payout_link.rs | 39 ++- crates/router/src/core/payouts.rs | 163 +++++++++- crates/router/src/core/payouts/validator.rs | 229 ++++++-------- crates/router/src/routes/payout_link.rs | 3 +- crates/router/src/services/api.rs | 42 ++- .../src/services/api/generic_link_response.rs | 16 +- crates/router/src/types/api/admin.rs | 26 +- .../down.sql | 3 + .../up.sql | 5 + 23 files changed, 794 insertions(+), 348 deletions(-) create mode 100644 migrations/2024-07-17-064610_add_allowed_domains_to_link_data/down.sql create mode 100644 migrations/2024-07-17-064610_add_allowed_domains_to_link_data/up.sql diff --git a/Cargo.lock b/Cargo.lock index b29894b49c39..0188b3d6f1c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6129,6 +6129,7 @@ dependencies = [ "events", "external_services", "futures 0.3.30", + "globset", "hex", "http 0.2.12", "hyper 0.14.28", diff --git a/api-reference/openapi_spec.json b/api-reference/openapi_spec.json index 12b3d2a05fd7..0014042b2335 100644 --- a/api-reference/openapi_spec.json +++ b/api-reference/openapi_spec.json @@ -6824,11 +6824,22 @@ }, { "type": "object", + "required": [ + "allowed_domains" + ], "properties": { "domain_name": { "type": "string", "description": "Custom domain name to be used for hosting the link", "nullable": true + }, + "allowed_domains": { + "type": "array", + "items": { + "type": "string" + }, + "description": "A list of allowed domains (glob patterns) where this link can be embedded / opened from", + "uniqueItems": true } } } diff --git a/crates/api_models/src/admin.rs b/crates/api_models/src/admin.rs index 27bd098953c4..b22e0286395f 100644 --- a/crates/api_models/src/admin.rs +++ b/crates/api_models/src/admin.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; #[cfg(feature = "v2")] use common_utils::new_type; @@ -1390,11 +1390,39 @@ pub struct BusinessGenericLinkConfig { /// Custom domain name to be used for hosting the link pub domain_name: Option, + /// A list of allowed domains (glob patterns) where this link can be embedded / opened from + pub allowed_domains: HashSet, + #[serde(flatten)] #[schema(value_type = GenericLinkUiConfig)] pub ui_config: link_utils::GenericLinkUiConfig, } +impl BusinessGenericLinkConfig { + pub fn validate(&self) -> Result<(), &str> { + // Validate host domain name + let host_domain_valid = self + .domain_name + .clone() + .map(|host_domain| link_utils::validate_strict_domain(&host_domain)) + .unwrap_or(true); + if !host_domain_valid { + return Err("Invalid host domain name received"); + } + + let are_allowed_domains_valid = self + .allowed_domains + .clone() + .iter() + .all(|allowed_domain| link_utils::validate_wildcard_domain(allowed_domain)); + if !are_allowed_domains_valid { + return Err("Invalid allowed domain names received"); + } + + Ok(()) + } +} + #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PartialEq, ToSchema)] pub struct BusinessPaymentLinkConfig { /// Custom domain name to be used for hosting the link in your own domain diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index a9229c740240..848189cd8995 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -99,5 +99,21 @@ pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64; /// Minimum allowed length for MerchantReferenceId pub const MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 1; +/// Regex for matching a domain +/// Eg - +/// http://www.example.com +/// https://www.example.com +/// www.example.com +/// example.io +pub const STRICT_DOMAIN_REGEX: &str = r"^(https?://)?(([A-Za-z0-9][-A-Za-z0-9]\.)*[A-Za-z0-9][-A-Za-z0-9]*|(\d{1,3}\.){3}\d{1,3})+(:[0-9]{2,4})?$"; + +/// Regex for matching a wildcard domain +/// Eg - +/// *.example.com +/// *.subdomain.domain.com +/// *://example.com +/// *example.com +pub const WILDCARD_DOMAIN_REGEX: &str = r"^((\*|https?)?://)?((\*\.|[A-Za-z0-9][-A-Za-z0-9]*\.)*[A-Za-z0-9][-A-Za-z0-9]*|((\d{1,3}|\*)\.){3}(\d{1,3}|\*)|\*)(:\*|:[0-9]{2,4})?(/\*)?$"; + /// Maximum allowed length for MerchantName pub const MAX_ALLOWED_MERCHANT_NAME_LENGTH: usize = 64; diff --git a/crates/common_utils/src/link_utils.rs b/crates/common_utils/src/link_utils.rs index 2960209dfc90..e95832eeba9b 100644 --- a/crates/common_utils/src/link_utils.rs +++ b/crates/common_utils/src/link_utils.rs @@ -1,4 +1,4 @@ -//! Common +//! This module has common utilities for links in HyperSwitch use std::{collections::HashSet, primitive::i64}; @@ -13,10 +13,13 @@ use diesel::{ }; use error_stack::{report, ResultExt}; use masking::Secret; +use regex::Regex; +#[cfg(feature = "logs")] +use router_env::logger; use serde::Serialize; use utoipa::ToSchema; -use crate::{errors::ParsingError, id_type, types::MinorUnit}; +use crate::{consts, errors::ParsingError, id_type, types::MinorUnit}; #[derive( Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq, FromSqlRow, AsExpression, ToSchema, @@ -162,6 +165,8 @@ pub struct PayoutLinkData { pub amount: MinorUnit, /// Payout currency pub currency: enums::Currency, + /// A list of allowed domains (glob patterns) where this link can be embedded / opened from + pub allowed_domains: HashSet, } crate::impl_to_sql_from_sql_json!(PayoutLinkData); @@ -209,3 +214,140 @@ pub struct EnabledPaymentMethod { #[schema(value_type = HashSet)] pub payment_method_types: HashSet, } + +/// Util function for validating a domain without any wildcard characters. +pub fn validate_strict_domain(domain: &str) -> bool { + Regex::new(consts::STRICT_DOMAIN_REGEX) + .map(|regex| regex.is_match(domain)) + .map_err(|err| { + let err_msg = format!("Invalid strict domain regex: {err:?}"); + #[cfg(feature = "logs")] + logger::error!(err_msg); + err_msg + }) + .unwrap_or(false) +} + +/// Util function for validating a domain with "*" wildcard characters. +pub fn validate_wildcard_domain(domain: &str) -> bool { + Regex::new(consts::WILDCARD_DOMAIN_REGEX) + .map(|regex| regex.is_match(domain)) + .map_err(|err| { + let err_msg = format!("Invalid strict domain regex: {err:?}"); + #[cfg(feature = "logs")] + logger::error!(err_msg); + err_msg + }) + .unwrap_or(false) +} + +#[cfg(test)] +mod domain_tests { + use regex::Regex; + + use super::*; + + #[test] + fn test_validate_strict_domain_regex() { + assert!( + Regex::new(consts::STRICT_DOMAIN_REGEX).is_ok(), + "Strict domain regex is invalid" + ); + } + + #[test] + fn test_validate_wildcard_domain_regex() { + assert!( + Regex::new(consts::WILDCARD_DOMAIN_REGEX).is_ok(), + "Wildcard domain regex is invalid" + ); + } + + #[test] + fn test_validate_strict_domain() { + let valid_domains = vec![ + "example.com", + "example.subdomain.com", + "https://example.com:8080", + "http://example.com", + "example.com:8080", + "example.com:443", + "localhost:443", + "127.0.0.1:443", + ]; + + for domain in valid_domains { + assert!( + validate_strict_domain(domain), + "Could not validate strict domain: {}", + domain + ); + } + + let invalid_domains = vec![ + "", + "invalid.domain.", + "not_a_domain", + "http://example.com/path?query=1#fragment", + "127.0.0.1.2:443", + ]; + + for domain in invalid_domains { + assert!( + !validate_strict_domain(domain), + "Could not validate invalid strict domain: {}", + domain + ); + } + } + + #[test] + fn test_validate_wildcard_domain() { + let valid_domains = vec![ + "example.com", + "example.subdomain.com", + "https://example.com:8080", + "http://example.com", + "example.com:8080", + "example.com:443", + "localhost:443", + "127.0.0.1:443", + "*.com", + "example.*.com", + "example.com:*", + "*:443", + "localhost:*", + "127.0.0.*:*", + "*:*", + ]; + + for domain in valid_domains { + assert!( + validate_wildcard_domain(domain), + "Could not validate wildcard domain: {}", + domain + ); + } + + let invalid_domains = vec![ + "", + "invalid.domain.", + "not_a_domain", + "http://example.com/path?query=1#fragment", + "*.", + ".*", + "example.com:*:", + "*:443:", + ":localhost:*", + "127.00.*:*", + ]; + + for domain in invalid_domains { + assert!( + !validate_wildcard_domain(domain), + "Could not validate invalid wildcard domain: {}", + domain + ); + } + } +} diff --git a/crates/hyperswitch_domain_models/src/api.rs b/crates/hyperswitch_domain_models/src/api.rs index bb768d21dd00..07d9337e4508 100644 --- a/crates/hyperswitch_domain_models/src/api.rs +++ b/crates/hyperswitch_domain_models/src/api.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::{collections::HashSet, fmt::Display}; use common_utils::{ events::{ApiEventMetric, ApiEventsType}, @@ -59,7 +59,13 @@ pub struct PaymentLinkStatusData { } #[derive(Debug, Eq, PartialEq)] -pub enum GenericLinks { +pub struct GenericLinks { + pub allowed_domains: HashSet, + pub data: GenericLinksData, +} + +#[derive(Debug, Eq, PartialEq)] +pub enum GenericLinksData { ExpiredLink(GenericExpiredLinkData), PaymentMethodCollect(GenericLinkFormData), PayoutLink(GenericLinkFormData), @@ -67,12 +73,12 @@ pub enum GenericLinks { PaymentMethodCollectStatus(GenericLinkStatusData), } -impl Display for GenericLinks { +impl Display for GenericLinksData { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, "{}", - match self { + match *self { Self::ExpiredLink(_) => "ExpiredLink", Self::PaymentMethodCollect(_) => "PaymentMethodCollect", Self::PayoutLink(_) => "PayoutLink", diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs index 782376519c29..e660850a9839 100644 --- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs +++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs @@ -271,6 +271,8 @@ pub enum ApiErrorResponse { InvalidCookie, #[error(error_type = ErrorType::InvalidRequestError, code = "IR_27", message = "Extended card info does not exist")] ExtendedCardInfoNotFound, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_28", message = "{message}")] + LinkConfigurationError { message: String }, #[error(error_type = ErrorType::ServerNotAvailable, code = "IE", message = "{reason} as data mismatched for {field_names}", ignore = "status_code")] IntegrityCheckFailed { reason: String, @@ -615,6 +617,9 @@ impl ErrorSwitch for ApiErrorRespon Self::ExtendedCardInfoNotFound => { AER::NotFound(ApiError::new("IR", 27, "Extended card info does not exist", None)) } + Self::LinkConfigurationError { message } => { + AER::BadRequest(ApiError::new("IR", 28, message, None)) + }, Self::IntegrityCheckFailed { reason, field_names, diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 108b035ec0f3..1212869600bf 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -58,6 +58,7 @@ dyn-clone = "1.0.17" encoding_rs = "0.8.33" error-stack = "0.4.1" futures = "0.3.30" +globset = "0.4.14" hex = "0.4.3" http = "0.2.12" hyper = "0.14.28" diff --git a/crates/router/src/compatibility/stripe/errors.rs b/crates/router/src/compatibility/stripe/errors.rs index f72b71570a01..e0a9b6c8f87a 100644 --- a/crates/router/src/compatibility/stripe/errors.rs +++ b/crates/router/src/compatibility/stripe/errors.rs @@ -264,6 +264,8 @@ pub enum StripeErrorCode { PaymentMethodDeleteFailed, #[error(error_type = StripeErrorType::InvalidRequestError, code = "", message = "Extended card info does not exist")] ExtendedCardInfoNotFound, + #[error(error_type = StripeErrorType::InvalidRequestError, code = "not_configured", message = "{message}")] + LinkConfigurationError { message: String }, #[error(error_type = StripeErrorType::ConnectorError, code = "CE", message = "{reason} as data mismatched for {field_names}")] IntegrityCheckFailed { reason: String, @@ -656,6 +658,9 @@ impl From for StripeErrorCode { Self::InvalidWalletToken { wallet_name } } errors::ApiErrorResponse::ExtendedCardInfoNotFound => Self::ExtendedCardInfoNotFound, + errors::ApiErrorResponse::LinkConfigurationError { message } => { + Self::LinkConfigurationError { message } + } errors::ApiErrorResponse::IntegrityCheckFailed { reason, field_names, @@ -742,7 +747,8 @@ impl actix_web::ResponseError for StripeErrorCode { | Self::InvalidConnectorConfiguration { .. } | Self::CurrencyConversionFailed | Self::PaymentMethodDeleteFailed - | Self::ExtendedCardInfoNotFound => StatusCode::BAD_REQUEST, + | Self::ExtendedCardInfoNotFound + | Self::LinkConfigurationError { .. } => StatusCode::BAD_REQUEST, Self::RefundFailed | Self::PayoutFailed | Self::PaymentLinkNotFound diff --git a/crates/router/src/compatibility/wrap.rs b/crates/router/src/compatibility/wrap.rs index 7c1bd7e67121..9ed03c6d2ce0 100644 --- a/crates/router/src/compatibility/wrap.rs +++ b/crates/router/src/compatibility/wrap.rs @@ -141,10 +141,11 @@ where } Ok(api::ApplicationResponse::GenericLinkForm(boxed_generic_link_data)) => { - let link_type = (boxed_generic_link_data).to_string(); - match services::generic_link_response::build_generic_link_html(*boxed_generic_link_data) - { - Ok(rendered_html) => api::http_response_html_data(rendered_html), + let link_type = (boxed_generic_link_data).data.to_string(); + match services::generic_link_response::build_generic_link_html( + boxed_generic_link_data.data, + ) { + Ok(rendered_html) => api::http_response_html_data(rendered_html, None), Err(_) => { api::http_response_err(format!("Error while rendering {} HTML page", link_type)) } @@ -155,7 +156,7 @@ where match *boxed_payment_link_data { api::PaymentLinkAction::PaymentLinkFormData(payment_link_data) => { match api::build_payment_link_html(payment_link_data) { - Ok(rendered_html) => api::http_response_html_data(rendered_html), + Ok(rendered_html) => api::http_response_html_data(rendered_html, None), Err(_) => api::http_response_err( r#"{ "error": { @@ -167,7 +168,7 @@ where } api::PaymentLinkAction::PaymentLinkStatus(payment_link_data) => { match api::get_payment_link_status(payment_link_data) { - Ok(rendered_html) => api::http_response_html_data(rendered_html), + Ok(rendered_html) => api::http_response_html_data(rendered_html, None), Err(_) => api::http_response_err( r#"{ "error": { diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index d8eb14006605..af6d469fd3af 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -2061,6 +2061,21 @@ pub async fn update_business_profile( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?; + let payout_link_config = request + .payout_link_config + .as_ref() + .map(|payout_conf| match payout_conf.config.validate() { + Ok(_) => payout_conf.encode_to_value().change_context( + errors::ApiErrorResponse::InvalidDataValue { + field_name: "payout_link_config", + }, + ), + Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData { + message: e.to_string() + })), + }) + .transpose()?; + let business_profile_update = storage::business_profile::BusinessProfileUpdate::Update { profile_name: request.profile_name, modified_at: Some(date_time::now()), @@ -2089,14 +2104,7 @@ pub async fn update_business_profile( .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "authentication_connector_details", })?, - payout_link_config: request - .payout_link_config - .as_ref() - .map(Encode::encode_to_value) - .transpose() - .change_context(errors::ApiErrorResponse::InvalidDataValue { - field_name: "payout_link_config", - })?, + payout_link_config, extended_card_info_config, use_billing_as_payment_method_billing: request.use_billing_as_payment_method_billing, collect_shipping_details_from_wallet_connector: request diff --git a/crates/router/src/core/generic_link/payout_link/initiate/script.js b/crates/router/src/core/generic_link/payout_link/initiate/script.js index a8050e90d6eb..731f3f76d0ac 100644 --- a/crates/router/src/core/generic_link/payout_link/initiate/script.js +++ b/crates/router/src/core/generic_link/payout_link/initiate/script.js @@ -1,153 +1,171 @@ // @ts-check -var widgets = null; -var payoutWidget = null; -// @ts-ignore -var publishableKey = window.__PAYOUT_DETAILS.publishable_key; -var hyper = null; +// Top level checks +var isFramed = false; +try { + isFramed = window.parent.location !== window.location; -/** - * Use - format date in "hh:mm AM/PM timezone MM DD, YYYY" - * @param {Date} date - **/ -function formatDate(date) { - var months = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ]; + // If parent's window object is restricted, DOMException is + // thrown which concludes that the webpage is iframed +} catch (err) { + isFramed = true; +} - var hours = date.getHours(); - var minutes = date.getMinutes(); - // @ts-ignore - minutes = minutes < 10 ? "0" + minutes : minutes; - var suffix = hours > 11 ? "PM" : "AM"; - hours = hours % 12; - hours = hours ? hours : 12; - var day = date.getDate(); - var month = months[date.getMonth()]; - var year = date.getUTCFullYear(); +// Remove the script from DOM incase it's not iframed +if (!isFramed) { + function initializePayoutSDK() { + var errMsg = "You are not allowed to view this content."; + var contentElement = document.getElementById("payout-link"); + if (contentElement instanceof HTMLDivElement) { + contentElement.innerHTML = errMsg; + } else { + document.body.innerHTML = errMsg; + } + } - // @ts-ignore - var locale = navigator.language || navigator.userLanguage; - var timezoneShorthand = date - .toLocaleDateString(locale, { - day: "2-digit", - timeZoneName: "long", - }) - .substring(4) - .split(" ") - .reduce(function (tz, c) { - return tz + c.charAt(0).toUpperCase(); - }, ""); + // webpage is iframed, good to load +} else { + var hyper = null; + var payoutWidget = null; + var widgets = null; + /** + * Use - format date in "hh:mm AM/PM timezone MM DD, YYYY" + * @param {Date} date + **/ + function formatDate(date) { + var months = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ]; - var formatted = - hours + - ":" + - minutes + - " " + - suffix + - " " + - timezoneShorthand + - " " + - month + - " " + - day + - ", " + - year; - return formatted; -} + var hours = date.getHours(); + var minutes = date.getMinutes(); + // @ts-ignore + minutes = minutes < 10 ? "0" + minutes : minutes; + var suffix = hours > 11 ? "PM" : "AM"; + hours = hours % 12; + hours = hours ? hours : 12; + var day = date.getDate(); + var month = months[date.getMonth()]; + var year = date.getUTCFullYear(); -/** - * Trigger - init - * Uses - * - Initialize SDK - * - Update document's icon - */ -function boot() { - // Initialize SDK - // @ts-ignore - if (window.Hyper) { - initializePayoutSDK(); + // @ts-ignore + var locale = navigator.language || navigator.userLanguage; + var timezoneShorthand = date + .toLocaleDateString(locale, { + day: "2-digit", + timeZoneName: "long", + }) + .substring(4) + .split(" ") + .reduce(function (tz, c) { + return tz + c.charAt(0).toUpperCase(); + }, ""); + + var formatted = + hours + + ":" + + minutes + + " " + + suffix + + " " + + timezoneShorthand + + " " + + month + + " " + + day + + ", " + + year; + return formatted; } - // @ts-ignore - var payoutDetails = window.__PAYOUT_DETAILS; + /** + * Trigger - init + * Uses + * - Initialize SDK + * - Update document's icon + */ + function boot() { + // Initialize SDK + // @ts-ignore + if (window.Hyper) { + initializePayoutSDK(); + } + + // @ts-ignore + var payoutDetails = window.__PAYOUT_DETAILS; - // Attach document icon - if (payoutDetails.logo) { - var link = document.createElement("link"); - link.rel = "icon"; - link.href = payoutDetails.logo; - link.type = "image/x-icon"; - document.head.appendChild(link); + // Attach document icon + if (payoutDetails.logo) { + var link = document.createElement("link"); + link.rel = "icon"; + link.href = payoutDetails.logo; + link.type = "image/x-icon"; + document.head.appendChild(link); + } } -} -boot(); + boot(); -/** - * Trigger - post downloading SDK - * Uses - * - Initialize SDK - * - Create a payout widget - * - Mount it in DOM - **/ -function initializePayoutSDK() { - // @ts-ignore - var payoutDetails = window.__PAYOUT_DETAILS; - var clientSecret = payoutDetails.client_secret; - var appearance = { - variables: { - colorPrimary: payoutDetails?.theme?.primary_color || "rgb(0, 109, 249)", - fontFamily: "Work Sans, sans-serif", - fontSizeBase: "16px", - colorText: "rgb(51, 65, 85)", - colorTextSecondary: "#334155B3", - colorPrimaryText: "rgb(51, 65, 85)", - colorTextPlaceholder: "#33415550", - borderColor: "#33415550", - colorBackground: "rgb(255, 255, 255)", - }, - }; - // Instantiate - // @ts-ignore - hyper = window.Hyper(publishableKey, { - isPreloadEnabled: false, - }); - widgets = hyper.widgets({ - appearance: appearance, - clientSecret: clientSecret, - }); + /** + * Trigger - post downloading SDK + * Uses + * - Initialize SDK + * - Create a payout widget + * - Mount it in DOM + **/ + function initializePayoutSDK() { + // @ts-ignore + var payoutDetails = window.__PAYOUT_DETAILS; + var clientSecret = payoutDetails.client_secret; + var publishableKey = payoutDetails.publishable_key; + var appearance = { + variables: { + colorPrimary: payoutDetails?.theme?.primary_color || "rgb(0, 109, 249)", + fontFamily: "Work Sans, sans-serif", + fontSizeBase: "16px", + colorText: "rgb(51, 65, 85)", + }, + }; + // @ts-ignore + hyper = window.Hyper(publishableKey, { + isPreloadEnabled: false, + }); + widgets = hyper.widgets({ + appearance: appearance, + clientSecret: clientSecret, + }); - // Create payment method collect widget - let sessionExpiry = formatDate(new Date(payoutDetails.session_expiry)); - var payoutOptions = { - linkId: payoutDetails.payout_link_id, - payoutId: payoutDetails.payout_id, - customerId: payoutDetails.customer_id, - theme: payoutDetails.theme, - collectorName: payoutDetails.merchant_name, - logo: payoutDetails.logo, - enabledPaymentMethods: payoutDetails.enabled_payment_methods, - returnUrl: payoutDetails.return_url, - sessionExpiry, - amount: payoutDetails.amount, - currency: payoutDetails.currency, - flow: "PayoutLinkInitiate", - }; - payoutWidget = widgets.create("paymentMethodCollect", payoutOptions); + // Create payment method collect widget + let sessionExpiry = formatDate(new Date(payoutDetails.session_expiry)); + var payoutOptions = { + linkId: payoutDetails.payout_link_id, + payoutId: payoutDetails.payout_id, + customerId: payoutDetails.customer_id, + theme: payoutDetails.theme, + collectorName: payoutDetails.merchant_name, + logo: payoutDetails.logo, + enabledPaymentMethods: payoutDetails.enabled_payment_methods, + returnUrl: payoutDetails.return_url, + sessionExpiry, + amount: payoutDetails.amount, + currency: payoutDetails.currency, + flow: "PayoutLinkInitiate", + }; + payoutWidget = widgets.create("paymentMethodCollect", payoutOptions); - // Mount - if (payoutWidget !== null) { - payoutWidget.mount("#payout-link"); + // Mount + if (payoutWidget !== null) { + payoutWidget.mount("#payout-link"); + } } } diff --git a/crates/router/src/core/generic_link/payout_link/status/styles.css b/crates/router/src/core/generic_link/payout_link/status/styles.css index cf2d89b6a30b..d56685531294 100644 --- a/crates/router/src/core/generic_link/payout_link/status/styles.css +++ b/crates/router/src/core/generic_link/payout_link/status/styles.css @@ -78,9 +78,9 @@ body { } #resource-info-container { - width: calc(100% - 80px); + width: 100%; border-top: 1px solid rgb(231, 234, 241); - padding: 20px 40px; + padding: 20px 0; } #resource-info { display: flex; @@ -88,7 +88,7 @@ body { } #info-key { text-align: right; - font-size: 15px; + font-size: 14px; min-width: 10ch; } #info-val { @@ -101,13 +101,33 @@ body { margin-top: 40px; } -@media only screen and (max-width: 1199px) { +@media only screen and (max-width: 420px) { body { overflow-y: scroll; } + body { + justify-content: start; + } + .main { - width: auto; + width: 100%; min-width: 300px; } + + #status-card { + box-shadow: none; + } + + #info-key { + min-width: 12ch; + } + + #info-val { + font-size: 11px; + } + + #resource-info { + margin: 0 10px; + } } diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index 00b51b9f09e2..b9e21b616c0f 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; pub mod cards; pub mod migration; pub mod surcharge_decision_configs; @@ -13,7 +14,10 @@ use diesel_models::{ enums, GenericLinkNew, PaymentMethodCollectLink, PaymentMethodCollectLinkData, }; use error_stack::{report, ResultExt}; -use hyperswitch_domain_models::payments::{payment_attempt::PaymentAttempt, PaymentIntent}; +use hyperswitch_domain_models::{ + api::{GenericLinks, GenericLinksData}, + payments::{payment_attempt::PaymentAttempt, PaymentIntent}, +}; use masking::PeekInterface; use router_env::{instrument, tracing}; use time::Duration; @@ -27,7 +31,7 @@ use crate::{ pm_auth as core_pm_auth, }, routes::{app::StorageInterface, SessionState}, - services::{self, GenericLinks}, + services, types::{ api::{self, payments}, domain, storage, @@ -246,7 +250,10 @@ pub async fn render_pm_collect_link( theme: link_data.ui_config.theme.unwrap_or(default_ui_config.theme), }; Ok(services::ApplicationResponse::GenericLinkForm(Box::new( - GenericLinks::ExpiredLink(expired_link_data), + GenericLinks { + allowed_domains: HashSet::from([]), + data: GenericLinksData::ExpiredLink(expired_link_data), + }, ))) // else, send back form link @@ -304,7 +311,11 @@ pub async fn render_pm_collect_link( html_meta_tags: String::new(), }; Ok(services::ApplicationResponse::GenericLinkForm(Box::new( - GenericLinks::PaymentMethodCollect(generic_form_data), + GenericLinks { + allowed_domains: HashSet::from([]), + + data: GenericLinksData::PaymentMethodCollect(generic_form_data), + }, ))) } } @@ -345,7 +356,11 @@ pub async fn render_pm_collect_link( css_data: serialized_css_content, }; Ok(services::ApplicationResponse::GenericLinkForm(Box::new( - GenericLinks::PaymentMethodCollectStatus(generic_status_data), + GenericLinks { + allowed_domains: HashSet::from([]), + + data: GenericLinksData::PaymentMethodCollectStatus(generic_status_data), + }, ))) } } diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index e91a55e860c8..3de8e505499b 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -1,5 +1,9 @@ -use std::collections::{HashMap, HashSet}; +use std::{ + cmp::Ordering, + collections::{HashMap, HashSet}, +}; +use actix_web::http::header; use api_models::payouts; use common_utils::{ ext_traits::{Encode, OptionExt}, @@ -8,14 +12,15 @@ use common_utils::{ }; use diesel_models::PayoutLinkUpdate; use error_stack::ResultExt; +use hyperswitch_domain_models::api::{GenericLinks, GenericLinksData}; use super::errors::{RouterResponse, StorageErrorExt}; use crate::{ configs::settings::{PaymentMethodFilterKey, PaymentMethodFilters}, - core::payments::helpers, + core::{payments::helpers, payouts::validator}, errors, routes::{app::StorageInterface, SessionState}, - services::{self, GenericLinks}, + services, types::domain, }; @@ -24,6 +29,7 @@ pub async fn initiate_payout_link( merchant_account: domain::MerchantAccount, key_store: domain::MerchantKeyStore, req: payouts::PayoutLinkInitiateRequest, + request_headers: &header::HeaderMap, ) -> RouterResponse { let db: &dyn StorageInterface = &*state.store; let merchant_id = &merchant_account.merchant_id; @@ -59,6 +65,8 @@ pub async fn initiate_payout_link( message: "payout link not found".to_string(), })?; + validator::validate_payout_link_render_request(request_headers, &payout_link)?; + // Check status and return form data accordingly let has_expired = common_utils::date_time::now() > payout_link.expiry; let status = payout_link.link_status.clone(); @@ -97,7 +105,10 @@ pub async fn initiate_payout_link( } Ok(services::ApplicationResponse::GenericLinkForm(Box::new( - GenericLinks::ExpiredLink(expired_link_data), + GenericLinks { + allowed_domains: (link_data.allowed_domains), + data: GenericLinksData::ExpiredLink(expired_link_data), + }, ))) } @@ -146,9 +157,17 @@ pub async fn initiate_payout_link( }; // Fetch enabled payout methods from the request. If not found, fetch the enabled payout methods from MCA, // If none are configured for merchant connector accounts, fetch them from the default enabled payout methods. - let enabled_payment_methods = link_data + let mut enabled_payment_methods = link_data .enabled_payment_methods .unwrap_or(fallback_enabled_payout_methods.to_vec()); + + // Sort payment methods (cards first) + enabled_payment_methods.sort_by(|a, b| match (a.payment_method, b.payment_method) { + (_, common_enums::PaymentMethod::Card) => Ordering::Greater, + (common_enums::PaymentMethod::Card, _) => Ordering::Less, + _ => Ordering::Equal, + }); + let js_data = payouts::PayoutLinkDetails { publishable_key: masking::Secret::new(merchant_account.publishable_key), client_secret: link_data.client_secret.clone(), @@ -186,7 +205,10 @@ pub async fn initiate_payout_link( html_meta_tags: String::new(), }; Ok(services::ApplicationResponse::GenericLinkForm(Box::new( - GenericLinks::PayoutLink(generic_form_data), + GenericLinks { + allowed_domains: (link_data.allowed_domains), + data: GenericLinksData::PayoutLink(generic_form_data), + }, ))) } @@ -225,7 +247,10 @@ pub async fn initiate_payout_link( css_data: serialized_css_content, }; Ok(services::ApplicationResponse::GenericLinkForm(Box::new( - GenericLinks::PayoutLinkStatus(generic_status_data), + GenericLinks { + allowed_domains: (link_data.allowed_domains), + data: GenericLinksData::PayoutLinkStatus(generic_status_data), + }, ))) } } diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 848d57bce874..e36036eb798c 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -5,38 +5,41 @@ pub mod retry; pub mod validator; use std::vec::IntoIter; -use api_models::{self, enums as api_enums, payouts::PayoutLinkResponse}; +use api_models::{self, admin, enums as api_enums, payouts::PayoutLinkResponse}; use common_utils::{ consts, crypto::Encryptable, ext_traits::{AsyncExt, ValueExt}, - link_utils::PayoutLinkStatus, + id_type::CustomerId, + link_utils::{GenericLinkStatus, GenericLinkUiConfig, PayoutLinkData, PayoutLinkStatus}, pii, types::MinorUnit, }; -use diesel_models::{enums as storage_enums, generic_link::PayoutLink}; +use diesel_models::{ + enums as storage_enums, + generic_link::{GenericLinkNew, PayoutLink}, +}; use error_stack::{report, ResultExt}; #[cfg(feature = "olap")] use futures::future::join_all; #[cfg(feature = "olap")] use hyperswitch_domain_models::errors::StorageError; -use masking::PeekInterface; +use masking::{PeekInterface, Secret}; #[cfg(feature = "payout_retry")] use retry::GsmValidation; use router_env::{instrument, logger, tracing}; use scheduler::utils as pt_utils; use serde_json; +use time::Duration; -use super::{ - errors::{ConnectorErrorExt, StorageErrorExt}, - payments::customers, -}; #[cfg(feature = "olap")] use crate::types::domain::behaviour::Conversion; use crate::{ core::{ - errors::{self, CustomResult, RouterResponse, RouterResult}, - payments::{self, helpers as payment_helpers}, + errors::{ + self, ConnectorErrorExt, CustomResult, RouterResponse, RouterResult, StorageErrorExt, + }, + payments::{self, customers, helpers as payment_helpers}, utils as core_utils, }, db::StorageInterface, @@ -1106,7 +1109,7 @@ pub async fn create_recipient( add_external_account_addition_task( &*state.store, payout_data, - common_utils::date_time::now().saturating_add(time::Duration::seconds(consts::STRIPE_ACCOUNT_ONBOARDING_DELAY_IN_SECONDS)), + common_utils::date_time::now().saturating_add(Duration::seconds(consts::STRIPE_ACCOUNT_ONBOARDING_DELAY_IN_SECONDS)), ) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -2172,7 +2175,7 @@ pub async fn payout_create_db_entries( let payout_link = match req.payout_link { Some(true) => Some( - validator::create_payout_link( + create_payout_link( state, &business_profile, &customer_id, @@ -2479,3 +2482,139 @@ async fn validate_and_get_business_profile( }) } } + +#[allow(clippy::too_many_arguments)] +pub async fn create_payout_link( + state: &SessionState, + business_profile: &storage::BusinessProfile, + customer_id: &CustomerId, + merchant_id: &String, + req: &payouts::PayoutCreateRequest, + payout_id: &String, +) -> RouterResult { + let payout_link_config_req = req.payout_link_config.to_owned(); + + // Fetch all configs + let default_config = &state.conf.generic_link.payout_link; + let profile_config = business_profile + .payout_link_config + .as_ref() + .map(|config| { + config + .clone() + .parse_value::("BusinessPayoutLinkConfig") + }) + .transpose() + .change_context(errors::ApiErrorResponse::InvalidDataValue { + field_name: "payout_link_config in business_profile", + })?; + let profile_ui_config = profile_config.as_ref().map(|c| c.config.ui_config.clone()); + let ui_config = payout_link_config_req + .as_ref() + .and_then(|config| config.ui_config.clone()) + .or(profile_ui_config); + + // Validate allowed_domains presence + let allowed_domains = profile_config + .as_ref() + .map(|config| config.config.allowed_domains.to_owned()) + .get_required_value("allowed_domains") + .change_context(errors::ApiErrorResponse::LinkConfigurationError { + message: "Payout links cannot be used without setting allowed_domains in profile" + .to_string(), + })?; + + // Form data to be injected in the link + let (logo, merchant_name, theme) = match ui_config { + Some(config) => (config.logo, config.merchant_name, config.theme), + _ => (None, None, None), + }; + let payout_link_config = GenericLinkUiConfig { + logo, + merchant_name, + theme, + }; + let client_secret = utils::generate_id(consts::ID_LENGTH, "payout_link_secret"); + let base_url = profile_config + .as_ref() + .and_then(|c| c.config.domain_name.as_ref()) + .map(|domain| format!("https://{}", domain)) + .unwrap_or(state.base_url.clone()); + let session_expiry = req + .session_expiry + .as_ref() + .map_or(default_config.expiry, |expiry| *expiry); + let url = format!("{base_url}/payout_link/{merchant_id}/{payout_id}"); + let link = url::Url::parse(&url) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable_lazy(|| format!("Failed to form payout link URL - {}", url))?; + let req_enabled_payment_methods = payout_link_config_req + .as_ref() + .and_then(|req| req.enabled_payment_methods.to_owned()); + let amount = req + .amount + .as_ref() + .get_required_value("amount") + .attach_printable("amount is a required value when creating payout links")?; + let currency = req + .currency + .as_ref() + .get_required_value("currency") + .attach_printable("currency is a required value when creating payout links")?; + let payout_link_id = core_utils::get_or_generate_id( + "payout_link_id", + &payout_link_config_req + .as_ref() + .and_then(|config| config.payout_link_id.clone()), + "payout_link", + )?; + + let data = PayoutLinkData { + payout_link_id: payout_link_id.clone(), + customer_id: customer_id.clone(), + payout_id: payout_id.to_string(), + link, + client_secret: Secret::new(client_secret), + session_expiry, + ui_config: payout_link_config, + enabled_payment_methods: req_enabled_payment_methods, + amount: MinorUnit::from(*amount), + currency: *currency, + allowed_domains, + }; + + create_payout_link_db_entry(state, merchant_id, &data, req.return_url.clone()).await +} + +pub async fn create_payout_link_db_entry( + state: &SessionState, + merchant_id: &String, + payout_link_data: &PayoutLinkData, + return_url: Option, +) -> RouterResult { + let db: &dyn StorageInterface = &*state.store; + + let link_data = serde_json::to_value(payout_link_data) + .map_err(|_| report!(errors::ApiErrorResponse::InternalServerError)) + .attach_printable("Failed to convert PayoutLinkData to Value")?; + + let payout_link = GenericLinkNew { + link_id: payout_link_data.payout_link_id.to_string(), + primary_reference: payout_link_data.payout_id.to_string(), + merchant_id: merchant_id.to_string(), + link_type: common_enums::GenericLinkType::PayoutLink, + link_status: GenericLinkStatus::PayoutLink(PayoutLinkStatus::Initiated), + link_data, + url: payout_link_data.link.to_string().into(), + return_url, + expiry: common_utils::date_time::now() + + Duration::seconds(payout_link_data.session_expiry.into()), + ..Default::default() + }; + + db.insert_payout_link(payout_link) + .await + .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { + message: "payout link already exists".to_string(), + }) +} diff --git a/crates/router/src/core/payouts/validator.rs b/crates/router/src/core/payouts/validator.rs index 3e69216167de..a1332181f6de 100644 --- a/crates/router/src/core/payouts/validator.rs +++ b/crates/router/src/core/payouts/validator.rs @@ -1,33 +1,25 @@ -use api_models::admin; +use std::collections::HashSet; + +use actix_web::http::header; #[cfg(feature = "olap")] use common_utils::errors::CustomResult; -use common_utils::{ - ext_traits::ValueExt, - id_type::CustomerId, - link_utils::{GenericLinkStatus, GenericLinkUiConfig, PayoutLinkData, PayoutLinkStatus}, - types::MinorUnit, -}; -use diesel_models::{ - business_profile::BusinessProfile, - generic_link::{GenericLinkNew, PayoutLink}, -}; +use diesel_models::generic_link::PayoutLink; use error_stack::{report, ResultExt}; +use globset::Glob; pub use hyperswitch_domain_models::errors::StorageError; -use masking::Secret; -use router_env::{instrument, tracing}; -use time::Duration; +use router_env::{instrument, logger, tracing}; +use url::Url; use super::helpers; use crate::{ - consts, core::{ - errors::{self, RouterResult, StorageErrorExt}, + errors::{self, RouterResult}, utils as core_utils, }, db::StorageInterface, routes::SessionState, types::{api::payouts, domain, storage}, - utils::{self, OptionExt}, + utils, }; #[instrument(skip(db))] @@ -192,128 +184,97 @@ pub(super) fn validate_payout_list_request_for_joins( Ok(()) } -#[allow(clippy::too_many_arguments)] -pub async fn create_payout_link( - state: &SessionState, - business_profile: &BusinessProfile, - customer_id: &CustomerId, - merchant_id: &String, - req: &payouts::PayoutCreateRequest, - payout_id: &String, -) -> RouterResult { - let payout_link_config_req = req.payout_link_config.to_owned(); - // Create payment method collect link ID - let payout_link_id = core_utils::get_or_generate_id( - "payout_link_id", - &payout_link_config_req - .as_ref() - .and_then(|config| config.payout_link_id.clone()), - "payout_link", - )?; - - // Fetch all configs - let default_config = &state.conf.generic_link.payout_link; - let profile_config = business_profile - .payout_link_config - .as_ref() - .map(|config| { - config - .clone() - .parse_value::("BusinessPayoutLinkConfig") - }) - .transpose() - .change_context(errors::ApiErrorResponse::InvalidDataValue { - field_name: "payout_link_config in business_profile", - })?; - let profile_ui_config = profile_config.as_ref().map(|c| c.config.ui_config.clone()); - let ui_config = payout_link_config_req - .as_ref() - .and_then(|config| config.ui_config.clone()) - .or(profile_ui_config); - - // Form data to be injected in the link - let (logo, merchant_name, theme) = match ui_config { - Some(config) => (config.logo, config.merchant_name, config.theme), - _ => (None, None, None), - }; - let payout_link_config = GenericLinkUiConfig { - logo, - merchant_name, - theme, - }; - let client_secret = utils::generate_id(consts::ID_LENGTH, "payout_link_secret"); - let base_url = profile_config - .as_ref() - .and_then(|c| c.config.domain_name.as_ref()) - .map(|domain| format!("https://{}", domain)) - .unwrap_or(state.base_url.clone()); - let session_expiry = req - .session_expiry - .as_ref() - .map_or(default_config.expiry, |expiry| *expiry); - let url = format!("{base_url}/payout_link/{merchant_id}/{payout_id}"); - let link = url::Url::parse(&url) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable_lazy(|| format!("Failed to form payout link URL - {}", url))?; - let req_enabled_payment_methods = payout_link_config_req - .as_ref() - .and_then(|req| req.enabled_payment_methods.to_owned()); - let amount = req - .amount - .as_ref() - .get_required_value("amount") - .attach_printable("amount is a required value when creating payout links")?; - let currency = req - .currency - .as_ref() - .get_required_value("currency") - .attach_printable("currency is a required value when creating payout links")?; - - let data = PayoutLinkData { - payout_link_id: payout_link_id.clone(), - customer_id: customer_id.clone(), - payout_id: payout_id.to_string(), - link, - client_secret: Secret::new(client_secret), - session_expiry, - ui_config: payout_link_config, - enabled_payment_methods: req_enabled_payment_methods, - amount: MinorUnit::from(*amount), - currency: *currency, - }; +pub fn validate_payout_link_render_request( + request_headers: &header::HeaderMap, + payout_link: &PayoutLink, +) -> RouterResult<()> { + let link_id = payout_link.link_id.to_owned(); + let link_data = payout_link.link_data.to_owned(); - create_payout_link_db_entry(state, merchant_id, &data, req.return_url.clone()).await -} + // Fetch destination is "iframe" + match request_headers.get("sec-fetch-dest").and_then(|v| v.to_str().ok()) { + Some("iframe") => Ok(()), + Some(requestor) => Err(report!(errors::ApiErrorResponse::AccessForbidden { + resource: "payout_link".to_string(), + })) + .attach_printable_lazy(|| { + format!( + "Access to payout_link [{}] is forbidden when requested through {}", + link_id, requestor + ) + }), + None => Err(report!(errors::ApiErrorResponse::AccessForbidden { + resource: "payout_link".to_string(), + })) + .attach_printable_lazy(|| { + format!( + "Access to payout_link [{}] is forbidden when sec-fetch-dest is not present in request headers", + link_id + ) + }), + }?; -pub async fn create_payout_link_db_entry( - state: &SessionState, - merchant_id: &String, - payout_link_data: &PayoutLinkData, - return_url: Option, -) -> RouterResult { - let db: &dyn StorageInterface = &*state.store; + // Validate origin / referer + let domain_in_req = { + let origin_or_referer = request_headers + .get("origin") + .or_else(|| request_headers.get("referer")) + .and_then(|v| v.to_str().ok()) + .ok_or_else(|| { + report!(errors::ApiErrorResponse::AccessForbidden { + resource: "payout_link".to_string(), + }) + }) + .attach_printable_lazy(|| { + format!( + "Access to payout_link [{}] is forbidden when origin or referer is not present in request headers", + link_id + ) + })?; - let link_data = serde_json::to_value(payout_link_data) - .map_err(|_| report!(errors::ApiErrorResponse::InternalServerError)) - .attach_printable("Failed to convert PayoutLinkData to Value")?; + let url = Url::parse(origin_or_referer) + .map_err(|_| { + report!(errors::ApiErrorResponse::AccessForbidden { + resource: "payout_link".to_string(), + }) + }) + .attach_printable_lazy(|| { + format!("Invalid URL found in request headers {}", origin_or_referer) + })?; - let payout_link = GenericLinkNew { - link_id: payout_link_data.payout_link_id.to_string(), - primary_reference: payout_link_data.payout_id.to_string(), - merchant_id: merchant_id.to_string(), - link_type: common_enums::GenericLinkType::PayoutLink, - link_status: GenericLinkStatus::PayoutLink(PayoutLinkStatus::Initiated), - link_data, - url: payout_link_data.link.to_string().into(), - return_url, - expiry: common_utils::date_time::now() - + Duration::seconds(payout_link_data.session_expiry.into()), - ..Default::default() + url.host_str() + .and_then(|host| url.port().map(|port| format!("{}:{}", host, port))) + .or_else(|| url.host_str().map(String::from)) + .ok_or_else(|| { + report!(errors::ApiErrorResponse::AccessForbidden { + resource: "payout_link".to_string(), + }) + }) + .attach_printable_lazy(|| { + format!("host or port not found in request headers {:?}", url) + })? }; - db.insert_payout_link(payout_link) - .await - .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { - message: "payout link already exists".to_string(), + if is_domain_allowed(&domain_in_req, link_data.allowed_domains) { + Ok(()) + } else { + Err(report!(errors::ApiErrorResponse::AccessForbidden { + resource: "payout_link".to_string(), + })) + .attach_printable_lazy(|| { + format!( + "Access to payout_link [{}] is forbidden from requestor - {}", + link_id, domain_in_req + ) }) + } +} + +fn is_domain_allowed(domain: &str, allowed_domains: HashSet) -> bool { + allowed_domains.iter().any(|allowed_domain| { + Glob::new(allowed_domain) + .map(|glob| glob.compile_matcher().is_match(domain)) + .map_err(|err| logger::error!("Invalid glob pattern! - {:?}", err)) + .unwrap_or(false) + }) } diff --git a/crates/router/src/routes/payout_link.rs b/crates/router/src/routes/payout_link.rs index 34850bdea6e7..e3a0327c3298 100644 --- a/crates/router/src/routes/payout_link.rs +++ b/crates/router/src/routes/payout_link.rs @@ -23,13 +23,14 @@ pub async fn render_payout_link( merchant_id: merchant_id.clone(), payout_id, }; + let headers = req.headers(); Box::pin(api::server_wrap( flow, state, &req, payload.clone(), |state, auth, req, _| { - initiate_payout_link(state, auth.merchant_account, auth.key_store, req) + initiate_payout_link(state, auth.merchant_account, auth.key_store, req, headers) }, &auth::MerchantIdAuth(merchant_id), api_locking::LockAction::NotApplicable, diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 5456d4c25026..5dc46d356525 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -13,8 +13,9 @@ use std::{ use actix_http::header::HeaderMap; use actix_web::{ - body, http::header::HeaderValue, web, FromRequest, HttpRequest, HttpResponse, Responder, - ResponseError, + body, + http::header::{HeaderName, HeaderValue}, + web, FromRequest, HttpRequest, HttpResponse, Responder, ResponseError, }; use api_models::enums::{CaptureMethod, PaymentMethodType}; pub use client::{proxy_bypass_urls, ApiClient, MockApiClient, ProxyClient}; @@ -1049,9 +1050,18 @@ where } Ok(ApplicationResponse::GenericLinkForm(boxed_generic_link_data)) => { - let link_type = (boxed_generic_link_data).to_string(); - match build_generic_link_html(*boxed_generic_link_data) { - Ok(rendered_html) => http_response_html_data(rendered_html), + let link_type = boxed_generic_link_data.data.to_string(); + match build_generic_link_html(boxed_generic_link_data.data) { + Ok(rendered_html) => { + let domains_str = boxed_generic_link_data + .allowed_domains + .into_iter() + .collect::>() + .join(" "); + let csp_header = format!("frame-ancestors 'self' {};", domains_str); + let headers = HashSet::from([("content-security-policy", csp_header)]); + http_response_html_data(rendered_html, Some(headers)) + } Err(_) => { http_response_err(format!("Error while rendering {} HTML page", link_type)) } @@ -1062,7 +1072,7 @@ where match *boxed_payment_link_data { PaymentLinkAction::PaymentLinkFormData(payment_link_data) => { match build_payment_link_html(payment_link_data) { - Ok(rendered_html) => http_response_html_data(rendered_html), + Ok(rendered_html) => http_response_html_data(rendered_html, None), Err(_) => http_response_err( r#"{ "error": { @@ -1074,7 +1084,7 @@ where } PaymentLinkAction::PaymentLinkStatus(payment_link_data) => { match get_payment_link_status(payment_link_data) { - Ok(rendered_html) => http_response_html_data(rendered_html), + Ok(rendered_html) => http_response_html_data(rendered_html, None), Err(_) => http_response_err( r#"{ "error": { @@ -1231,8 +1241,22 @@ pub fn http_response_file_data( HttpResponse::Ok().content_type(content_type).body(res) } -pub fn http_response_html_data(res: T) -> HttpResponse { - HttpResponse::Ok().content_type(mime::TEXT_HTML).body(res) +pub fn http_response_html_data( + res: T, + optional_headers: Option>, +) -> HttpResponse { + let mut res_builder = HttpResponse::Ok(); + res_builder.content_type(mime::TEXT_HTML); + + if let Some(headers) = optional_headers { + for (key, value) in headers { + if let Ok(header_val) = HeaderValue::try_from(value) { + res_builder.insert_header((HeaderName::from_static(key), header_val)); + } + } + } + + res_builder.body(res) } pub fn http_response_ok() -> HttpResponse { diff --git a/crates/router/src/services/api/generic_link_response.rs b/crates/router/src/services/api/generic_link_response.rs index fb8b3b3ec74d..497926481ab2 100644 --- a/crates/router/src/services/api/generic_link_response.rs +++ b/crates/router/src/services/api/generic_link_response.rs @@ -1,25 +1,27 @@ use common_utils::errors::CustomResult; use error_stack::ResultExt; +use hyperswitch_domain_models::api::{ + GenericExpiredLinkData, GenericLinkFormData, GenericLinkStatusData, GenericLinksData, +}; use tera::{Context, Tera}; -use super::{GenericExpiredLinkData, GenericLinkFormData, GenericLinkStatusData, GenericLinks}; use crate::core::errors; pub fn build_generic_link_html( - boxed_generic_link_data: GenericLinks, + boxed_generic_link_data: GenericLinksData, ) -> CustomResult { match boxed_generic_link_data { - GenericLinks::ExpiredLink(link_data) => build_generic_expired_link_html(&link_data), + GenericLinksData::ExpiredLink(link_data) => build_generic_expired_link_html(&link_data), - GenericLinks::PaymentMethodCollect(pm_collect_data) => { + GenericLinksData::PaymentMethodCollect(pm_collect_data) => { build_pm_collect_link_html(&pm_collect_data) } - GenericLinks::PaymentMethodCollectStatus(pm_collect_data) => { + GenericLinksData::PaymentMethodCollectStatus(pm_collect_data) => { build_pm_collect_link_status_html(&pm_collect_data) } - GenericLinks::PayoutLink(payout_link_data) => build_payout_link_html(&payout_link_data), + GenericLinksData::PayoutLink(payout_link_data) => build_payout_link_html(&payout_link_data), - GenericLinks::PayoutLinkStatus(pm_collect_data) => { + GenericLinksData::PayoutLinkStatus(pm_collect_data) => { build_payout_link_status_html(&pm_collect_data) } } diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 55288e02f86f..884e47c6fc4a 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -9,7 +9,7 @@ pub use api_models::admin::{ ToggleKVResponse, WebhookDetails, }; use common_utils::ext_traits::{AsyncExt, Encode, ValueExt}; -use error_stack::ResultExt; +use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{merchant_key_store::MerchantKeyStore, type_encryption::decrypt}; use masking::{ExposeInterface, PeekInterface, Secret}; @@ -194,6 +194,21 @@ pub async fn create_business_profile( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt outgoing webhook custom HTTP headers")?; + let payout_link_config = request + .payout_link_config + .as_ref() + .map(|payout_conf| match payout_conf.config.validate() { + Ok(_) => payout_conf.encode_to_value().change_context( + errors::ApiErrorResponse::InvalidDataValue { + field_name: "payout_link_config", + }, + ), + Err(e) => Err(report!(errors::ApiErrorResponse::InvalidRequestData { + message: e.to_string() + })), + }) + .transpose()?; + Ok(storage::business_profile::BusinessProfileNew { profile_id, merchant_id: merchant_account.merchant_id, @@ -246,14 +261,7 @@ pub async fn create_business_profile( .change_context(errors::ApiErrorResponse::InvalidDataValue { field_name: "authentication_connector_details", })?, - payout_link_config: request - .payout_link_config - .as_ref() - .map(Encode::encode_to_value) - .transpose() - .change_context(errors::ApiErrorResponse::InvalidDataValue { - field_name: "payout_link_config", - })?, + payout_link_config, is_connector_agnostic_mit_enabled: request.is_connector_agnostic_mit_enabled, is_extended_card_info_enabled: None, extended_card_info_config: None, diff --git a/migrations/2024-07-17-064610_add_allowed_domains_to_link_data/down.sql b/migrations/2024-07-17-064610_add_allowed_domains_to_link_data/down.sql new file mode 100644 index 000000000000..623bec2a2f0f --- /dev/null +++ b/migrations/2024-07-17-064610_add_allowed_domains_to_link_data/down.sql @@ -0,0 +1,3 @@ +UPDATE generic_link +SET link_data = link_data - 'allowed_domains' +WHERE link_data -> 'allowed_domains' = '["*"]'::jsonb AND link_type = 'payout_link'; \ No newline at end of file diff --git a/migrations/2024-07-17-064610_add_allowed_domains_to_link_data/up.sql b/migrations/2024-07-17-064610_add_allowed_domains_to_link_data/up.sql new file mode 100644 index 000000000000..affa2755d7ed --- /dev/null +++ b/migrations/2024-07-17-064610_add_allowed_domains_to_link_data/up.sql @@ -0,0 +1,5 @@ +UPDATE generic_link +SET link_data = jsonb_set(link_data, '{allowed_domains}', '["*"]'::jsonb) +WHERE + NOT link_data ? 'allowed_domains' + AND link_type = 'payout_link'; \ No newline at end of file From 67bfb1cfecd4a4ad8503eaf57837073bb1980bdd Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Wed, 17 Jul 2024 22:43:51 +0530 Subject: [PATCH 07/26] feat(router): Add support for passing the domain dynamically in the session call (#5347) --- crates/api_models/src/payments.rs | 1 + .../src/core/payments/flows/session_flow.rs | 61 ++++++++++++++++--- crates/router/src/lib.rs | 1 + crates/router/src/types/transformers.rs | 6 +- 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index c04af2044931..619f345a3336 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -745,6 +745,7 @@ pub struct HeaderPayload { pub x_hs_latency: Option, pub browser_name: Option, pub x_client_platform: Option, + pub x_merchant_domain: Option, } impl HeaderPayload { diff --git a/crates/router/src/core/payments/flows/session_flow.rs b/crates/router/src/core/payments/flows/session_flow.rs index 883203ad99ed..4441e349d9f4 100644 --- a/crates/router/src/core/payments/flows/session_flow.rs +++ b/crates/router/src/core/payments/flows/session_flow.rs @@ -210,6 +210,7 @@ async fn create_applepay_session_token( apple_pay_merchant_cert_key, apple_pay_merchant_identifier, merchant_business_country, + merchant_configured_domain_optional, ) = match apple_pay_metadata { payment_types::ApplepaySessionTokenMetadata::ApplePayCombined( apple_pay_combined_metadata, @@ -232,7 +233,7 @@ async fn create_applepay_session_token( let apple_pay_session_request = get_session_request_for_simplified_apple_pay( merchant_identifier.clone(), - session_token_data, + session_token_data.clone(), ); let apple_pay_merchant_cert = state @@ -256,6 +257,7 @@ async fn create_applepay_session_token( apple_pay_merchant_cert_key, merchant_identifier, merchant_business_country, + Some(session_token_data.initiative_context), ) } payment_types::ApplePayCombinedMetadata::Manual { @@ -264,8 +266,10 @@ async fn create_applepay_session_token( } => { logger::info!("Apple pay manual flow"); - let apple_pay_session_request = - get_session_request_for_manual_apple_pay(session_token_data.clone()); + let apple_pay_session_request = get_session_request_for_manual_apple_pay( + session_token_data.clone(), + header_payload.x_merchant_domain.clone(), + ); let merchant_business_country = session_token_data.merchant_business_country; @@ -276,6 +280,7 @@ async fn create_applepay_session_token( session_token_data.certificate_keys, session_token_data.merchant_identifier, merchant_business_country, + session_token_data.initiative_context, ) } }, @@ -284,6 +289,7 @@ async fn create_applepay_session_token( let apple_pay_session_request = get_session_request_for_manual_apple_pay( apple_pay_metadata.session_token_data.clone(), + header_payload.x_merchant_domain.clone(), ); let merchant_business_country = apple_pay_metadata @@ -299,6 +305,7 @@ async fn create_applepay_session_token( .clone(), apple_pay_metadata.session_token_data.merchant_identifier, merchant_business_country, + apple_pay_metadata.session_token_data.initiative_context, ) } }; @@ -383,9 +390,9 @@ async fn create_applepay_session_token( .attach_printable("Failed to obtain apple pay session request")?; let applepay_session_request = build_apple_pay_session_request( state, - apple_pay_session_request, - apple_pay_merchant_cert, - apple_pay_merchant_cert_key, + apple_pay_session_request.clone(), + apple_pay_merchant_cert.clone(), + apple_pay_merchant_cert_key.clone(), )?; let response = services::call_connector_api( @@ -395,10 +402,43 @@ async fn create_applepay_session_token( ) .await; - // logging the error if present in session call response - log_session_response_if_error(&response); + let updated_response = match ( + response.as_ref().ok(), + header_payload.x_merchant_domain.clone(), + ) { + (Some(Err(error)), Some(_)) => { + logger::error!( + "Retry apple pay session call with the merchant configured domain {error:?}" + ); + let merchant_configured_domain = merchant_configured_domain_optional + .ok_or(errors::ApiErrorResponse::InternalServerError) + .attach_printable( + "Failed to get initiative_context for apple pay session call retry", + )?; + let apple_pay_retry_session_request = + payment_types::ApplepaySessionRequest { + initiative_context: merchant_configured_domain, + ..apple_pay_session_request + }; + let applepay_retry_session_request = build_apple_pay_session_request( + state, + apple_pay_retry_session_request, + apple_pay_merchant_cert, + apple_pay_merchant_cert_key, + )?; + services::call_connector_api( + state, + applepay_retry_session_request, + "create_apple_pay_session_token", + ) + .await + } + _ => response, + }; - response + // logging the error if present in session call response + log_session_response_if_error(&updated_response); + updated_response .ok() .and_then(|apple_pay_res| { apple_pay_res @@ -454,6 +494,7 @@ fn get_session_request_for_simplified_apple_pay( fn get_session_request_for_manual_apple_pay( session_token_data: payment_types::SessionTokenInfo, + merchant_domain: Option, ) -> RouterResult { let initiative_context = session_token_data .initiative_context @@ -463,7 +504,7 @@ fn get_session_request_for_manual_apple_pay( merchant_identifier: session_token_data.merchant_identifier.clone(), display_name: session_token_data.display_name.clone(), initiative: session_token_data.initiative.to_string(), - initiative_context, + initiative_context: merchant_domain.unwrap_or(initiative_context), }) } diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 69a76be5bc0a..38000f7b6648 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -79,6 +79,7 @@ pub mod headers { pub const CONTENT_LENGTH: &str = "Content-Length"; pub const BROWSER_NAME: &str = "x-browser-name"; pub const X_CLIENT_PLATFORM: &str = "x-client-platform"; + pub const X_MERCHANT_DOMAIN: &str = "x-merchant-domain"; } pub mod pii { diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index 9a42916f68d3..d32f62ed0be0 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -21,7 +21,7 @@ use super::domain; use crate::{ core::errors, headers::{ - BROWSER_NAME, X_CLIENT_PLATFORM, X_CLIENT_SOURCE, X_CLIENT_VERSION, + BROWSER_NAME, X_CLIENT_PLATFORM, X_CLIENT_SOURCE, X_CLIENT_VERSION, X_MERCHANT_DOMAIN, X_PAYMENT_CONFIRM_SOURCE, }, services::authentication::get_header_value_by_key, @@ -1157,6 +1157,9 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload { .unwrap_or(api_enums::ClientPlatform::Unknown) }); + let x_merchant_domain = + get_header_value_by_key(X_MERCHANT_DOMAIN.into(), headers)?.map(|val| val.to_string()); + Ok(Self { payment_confirm_source, client_source, @@ -1164,6 +1167,7 @@ impl ForeignTryFrom<&HeaderMap> for payments::HeaderPayload { x_hs_latency: Some(x_hs_latency), browser_name, x_client_platform, + x_merchant_domain, }) } } From 24360b22efc308cc5fc6da7b4168f560a1dc8689 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Thu, 18 Jul 2024 00:19:36 +0530 Subject: [PATCH 08/26] refactor(router): Remove the locker call in the psync flow (#5348) --- crates/router/src/core/payments/operations.rs | 23 ++++++------------- .../payments/operations/payment_status.rs | 23 ++++++------------- 2 files changed, 14 insertions(+), 32 deletions(-) diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index f5bc40146554..be54ead0ac12 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -299,27 +299,18 @@ where #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a SessionState, - payment_data: &mut PaymentData, - storage_scheme: enums::MerchantStorageScheme, - merchant_key_store: &domain::MerchantKeyStore, - customer: &Option, - business_profile: Option<&diesel_models::business_profile::BusinessProfile>, + _state: &'a SessionState, + _payment_data: &mut PaymentData, + _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, + _customer: &Option, + _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRetrieveRequest>, Option, Option, )> { - helpers::make_pm_data( - Box::new(self), - state, - payment_data, - merchant_key_store, - customer, - storage_scheme, - business_profile, - ) - .await + Ok((Box::new(self), None, None)) } #[instrument(skip_all)] diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 5d4a5037b70e..5f20ccdf2779 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -85,27 +85,18 @@ impl Domain for PaymentStatus { #[instrument(skip_all)] async fn make_pm_data<'a>( &'a self, - state: &'a SessionState, - payment_data: &mut PaymentData, - storage_scheme: enums::MerchantStorageScheme, - merchant_key_store: &domain::MerchantKeyStore, - customer: &Option, - business_profile: Option<&diesel_models::business_profile::BusinessProfile>, + _state: &'a SessionState, + _payment_data: &mut PaymentData, + _storage_scheme: enums::MerchantStorageScheme, + _merchant_key_store: &domain::MerchantKeyStore, + _customer: &Option, + _business_profile: Option<&diesel_models::business_profile::BusinessProfile>, ) -> RouterResult<( BoxedOperation<'a, F, api::PaymentsRequest>, Option, Option, )> { - helpers::make_pm_data( - Box::new(self), - state, - payment_data, - merchant_key_store, - customer, - storage_scheme, - business_profile, - ) - .await + Ok((Box::new(self), None, None)) } #[instrument(skip_all)] From 1973e3769fceee5b5d28b52e3e2a3b7470ee7a68 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 18 Jul 2024 00:17:21 +0000 Subject: [PATCH 09/26] chore(version): 2024.07.18.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bddc346a70ff..421ef15d409a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.07.18.0 + +### Features + +- **core:** Payments core modification for open banking connectors ([#3947](https://github.com/juspay/hyperswitch/pull/3947)) ([`eb6f27d`](https://github.com/juspay/hyperswitch/commit/eb6f27d64e07f3f65b4e9a2f227898a238355477)) +- **globalsearch:** Added search_tags based filter for global search in dashboard ([#5341](https://github.com/juspay/hyperswitch/pull/5341)) ([`35c9b8a`](https://github.com/juspay/hyperswitch/commit/35c9b8afe1a09b858c79c0ce13cf5c24d200d3fd)) +- **payout_link:** Secure payout links using server side validations and client side headers ([#5219](https://github.com/juspay/hyperswitch/pull/5219)) ([`2d204c9`](https://github.com/juspay/hyperswitch/commit/2d204c9f7348c4ed121ab472ef1b5bb8d9d32d24)) +- **router:** Add support for passing the domain dynamically in the session call ([#5347](https://github.com/juspay/hyperswitch/pull/5347)) ([`67bfb1c`](https://github.com/juspay/hyperswitch/commit/67bfb1cfecd4a4ad8503eaf57837073bb1980bdd)) + +### Refactors + +- **connector:** Added amount conversion framework for checkout,adyen and globalpay ([#4974](https://github.com/juspay/hyperswitch/pull/4974)) ([`ecc862c`](https://github.com/juspay/hyperswitch/commit/ecc862c3543be37e2cc7959f450ca51770978ae5)) +- **cypress:** Nullify false positives ([#5303](https://github.com/juspay/hyperswitch/pull/5303)) ([`96edf52`](https://github.com/juspay/hyperswitch/commit/96edf52ca639178e01dc4c3e008611b847bc358f)) +- **router:** Remove the locker call in the psync flow ([#5348](https://github.com/juspay/hyperswitch/pull/5348)) ([`24360b2`](https://github.com/juspay/hyperswitch/commit/24360b22efc308cc5fc6da7b4168f560a1dc8689)) + +**Full Changelog:** [`2024.07.17.0...2024.07.18.0`](https://github.com/juspay/hyperswitch/compare/2024.07.17.0...2024.07.18.0) + +- - - + ## 2024.07.17.0 ### Features From ef1418f978835a8df149181bc5e19053775490f2 Mon Sep 17 00:00:00 2001 From: Sakil Mostak <73734619+Sakilmostak@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:00:23 +0530 Subject: [PATCH 10/26] feat(connector): [Itau Bank] Template for payment flows (#5304) --- config/config.example.toml | 1 + config/deployments/integration_test.toml | 1 + config/deployments/production.toml | 1 + config/deployments/sandbox.toml | 1 + config/development.toml | 2 + config/docker_compose.toml | 2 + crates/api_models/src/enums.rs | 1 + crates/common_enums/src/enums.rs | 1 + crates/hyperswitch_interfaces/src/configs.rs | 1 + crates/router/src/connector.rs | 3 +- crates/router/src/connector/itaubank.rs | 569 ++++++++++++++++++ .../src/connector/itaubank/transformers.rs | 216 +++++++ .../connector_integration_v2_impls.rs | 25 + crates/router/src/core/payments/flows.rs | 32 + crates/router/tests/connectors/itaubank.rs | 420 +++++++++++++ crates/router/tests/connectors/main.rs | 1 + .../router/tests/connectors/sample_auth.toml | 7 +- crates/test_utils/src/connector_auth.rs | 1 + loadtest/config/development.toml | 2 + scripts/add_connector.sh | 2 +- 20 files changed, 1283 insertions(+), 6 deletions(-) create mode 100644 crates/router/src/connector/itaubank.rs create mode 100644 crates/router/src/connector/itaubank/transformers.rs create mode 100644 crates/router/tests/connectors/itaubank.rs diff --git a/config/config.example.toml b/config/config.example.toml index 27b71e167433..443a805da0bc 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -213,6 +213,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" +itaubank.base_url = "https://sandbox.devportal.itau.com.br/itau-ep9-gtw-pix-recebimentos-ext-v2/v2" klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index d0e1d6828b87..fbcd7cf0364c 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -53,6 +53,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" +itaubank.base_url = "https://sandbox.devportal.itau.com.br/itau-ep9-gtw-pix-recebimentos-ext-v2/v2" klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index 5385a83cc11c..e3b84396bb3a 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -57,6 +57,7 @@ gocardless.base_url = "https://api.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://iata-pay.iata.org/api/v1" +itaubank.base_url = "https://sandbox.devportal.itau.com.br/itau-ep9-gtw-pix-recebimentos-ext-v2/v2" klarna.base_url = "https://api{{klarna_region}}.klarna.com/" mifinity.base_url = "https://secure.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 95f6bf263a97..2f428aa5c072 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -57,6 +57,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" +itaubank.base_url = "https://sandbox.devportal.itau.com.br/itau-ep9-gtw-pix-recebimentos-ext-v2/v2" klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" diff --git a/config/development.toml b/config/development.toml index dc3eb6a4073c..dd7859908d4e 100644 --- a/config/development.toml +++ b/config/development.toml @@ -125,6 +125,7 @@ cards = [ "gpayments", "helcim", "iatapay", + "itaubank", "mollie", "multisafepay", "netcetera", @@ -206,6 +207,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" +itaubank.base_url = "https://sandbox.devportal.itau.com.br/itau-ep9-gtw-pix-recebimentos-ext-v2/v2" klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" diff --git a/config/docker_compose.toml b/config/docker_compose.toml index 86cb7c0ae768..a6260055b716 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -142,6 +142,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" +itaubank.base_url = "https://sandbox.devportal.itau.com.br/itau-ep9-gtw-pix-recebimentos-ext-v2/v2" klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" @@ -222,6 +223,7 @@ cards = [ "gpayments", "helcim", "iatapay", + "itaubank", "mollie", "multisafepay", "netcetera", diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index b36e0ffe44af..1161602435b0 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -101,6 +101,7 @@ pub enum Connector { Gpayments, Helcim, Iatapay, + // Itaubank, template code for future usage Klarna, Mifinity, Mollie, diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 12281350af21..5be7dcd66f97 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -216,6 +216,7 @@ pub enum RoutableConnectors { Gocardless, Helcim, Iatapay, + // Itaubank, template code for future usage Klarna, Mifinity, Mollie, diff --git a/crates/hyperswitch_interfaces/src/configs.rs b/crates/hyperswitch_interfaces/src/configs.rs index 6eeb27a4f6c5..f9fb992d2553 100644 --- a/crates/hyperswitch_interfaces/src/configs.rs +++ b/crates/hyperswitch_interfaces/src/configs.rs @@ -44,6 +44,7 @@ pub struct Connectors { pub gpayments: ConnectorParams, pub helcim: ConnectorParams, pub iatapay: ConnectorParams, + pub itaubank: ConnectorParams, pub klarna: ConnectorParams, pub mifinity: ConnectorParams, pub mollie: ConnectorParams, diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index 12421ab0faa0..341a9215cd31 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -29,6 +29,7 @@ pub mod gocardless; pub mod gpayments; pub mod helcim; pub mod iatapay; +pub mod itaubank; pub mod klarna; pub mod mifinity; pub mod mollie; @@ -77,7 +78,7 @@ pub use self::{ coinbase::Coinbase, cryptopay::Cryptopay, cybersource::Cybersource, datatrans::Datatrans, dlocal::Dlocal, ebanx::Ebanx, fiserv::Fiserv, forte::Forte, globalpay::Globalpay, globepay::Globepay, gocardless::Gocardless, gpayments::Gpayments, helcim::Helcim, - iatapay::Iatapay, klarna::Klarna, mifinity::Mifinity, mollie::Mollie, + iatapay::Iatapay, itaubank::Itaubank, klarna::Klarna, mifinity::Mifinity, mollie::Mollie, multisafepay::Multisafepay, netcetera::Netcetera, nexinets::Nexinets, nmi::Nmi, noon::Noon, nuvei::Nuvei, opayo::Opayo, opennode::Opennode, payeezy::Payeezy, payme::Payme, payone::Payone, paypal::Paypal, payu::Payu, placetopay::Placetopay, powertranz::Powertranz, diff --git a/crates/router/src/connector/itaubank.rs b/crates/router/src/connector/itaubank.rs new file mode 100644 index 000000000000..eedb0f70fccc --- /dev/null +++ b/crates/router/src/connector/itaubank.rs @@ -0,0 +1,569 @@ +pub mod transformers; + +use common_utils::types::{AmountConvertor, StringMinorUnit, StringMinorUnitForConnector}; +use error_stack::{report, ResultExt}; +use masking::ExposeInterface; +use transformers as itaubank; + +use super::utils::{self as connector_utils}; +use crate::{ + configs::settings, + core::errors::{self, CustomResult}, + events::connector_api_logs::ConnectorEvent, + headers, + services::{ + self, + request::{self, Mask}, + ConnectorIntegration, ConnectorValidation, + }, + types::{ + self, + api::{self, ConnectorCommon, ConnectorCommonExt}, + ErrorResponse, RequestContent, Response, + }, + utils::BytesExt, +}; + +#[derive(Clone)] +pub struct Itaubank { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Itaubank { + pub fn new() -> &'static Self { + &Self { + amount_converter: &StringMinorUnitForConnector, + } + } +} + +impl api::Payment for Itaubank {} +impl api::PaymentSession for Itaubank {} +impl api::ConnectorAccessToken for Itaubank {} +impl api::MandateSetup for Itaubank {} +impl api::PaymentAuthorize for Itaubank {} +impl api::PaymentSync for Itaubank {} +impl api::PaymentCapture for Itaubank {} +impl api::PaymentVoid for Itaubank {} +impl api::Refund for Itaubank {} +impl api::RefundExecute for Itaubank {} +impl api::RefundSync for Itaubank {} +impl api::PaymentToken for Itaubank {} + +impl + ConnectorIntegration< + api::PaymentMethodToken, + types::PaymentMethodTokenizationData, + types::PaymentsResponseData, + > for Itaubank +{ +} + +impl ConnectorCommonExt for Itaubank +where + Self: ConnectorIntegration, +{ + fn build_headers( + &self, + req: &types::RouterData, + _connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + self.get_content_type().to_string().into(), + )]; + let mut api_key = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut api_key); + Ok(header) + } +} + +impl ConnectorCommon for Itaubank { + fn id(&self) -> &'static str { + "itaubank" + } + + fn get_currency_unit(&self) -> api::CurrencyUnit { + api::CurrencyUnit::Minor + } + + fn common_get_content_type(&self) -> &'static str { + "application/json" + } + + fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { + connectors.itaubank.base_url.as_ref() + } + + fn get_auth_header( + &self, + auth_type: &types::ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { + let auth = itaubank::ItaubankAuthType::try_from(auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + Ok(vec![( + headers::AUTHORIZATION.to_string(), + auth.api_key.expose().into_masked(), + )]) + } + + fn build_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + let response: itaubank::ItaubankErrorResponse = res + .response + .parse_struct("ItaubankErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + Ok(ErrorResponse { + status_code: res.status_code, + code: response.code, + message: response.message, + reason: response.reason, + attempt_status: None, + connector_transaction_id: None, + }) + } +} + +impl ConnectorValidation for Itaubank {} + +impl ConnectorIntegration + for Itaubank +{ +} + +impl ConnectorIntegration + for Itaubank +{ +} + +impl + ConnectorIntegration< + api::SetupMandate, + types::SetupMandateRequestData, + types::PaymentsResponseData, + > for Itaubank +{ +} + +impl ConnectorIntegration + for Itaubank +{ + fn get_headers( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + + let connector_router_data = itaubank::ItaubankRouterData::from((amount, req)); + let connector_req = itaubank::ItaubankPaymentsRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsAuthorizeType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsAuthorizeType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsAuthorizeRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: itaubank::ItaubankPaymentsResponse = res + .response + .parse_struct("Itaubank PaymentsAuthorizeResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Itaubank +{ + fn get_headers( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Get) + .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: itaubank::ItaubankPaymentsResponse = res + .response + .parse_struct("itaubank PaymentsSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Itaubank +{ + fn get_headers( + &self, + req: &types::PaymentsCaptureRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + _req: &types::PaymentsCaptureRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_request_body method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::PaymentsCaptureRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsCaptureType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsCaptureType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsCaptureType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsCaptureRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: itaubank::ItaubankPaymentsResponse = res + .response + .parse_struct("Itaubank PaymentsCaptureResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Itaubank +{ +} + +impl ConnectorIntegration + for Itaubank +{ + fn get_headers( + &self, + req: &types::RefundsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::RefundsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn get_request_body( + &self, + req: &types::RefundsRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let refund_amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_refund_amount, + req.request.currency, + )?; + + let connector_router_data = itaubank::ItaubankRouterData::from((refund_amount, req)); + let connector_req = itaubank::ItaubankRefundRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::RefundsRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + let request = services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::RefundExecuteType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundExecuteType::get_headers( + self, req, connectors, + )?) + .set_body(types::RefundExecuteType::get_request_body( + self, req, connectors, + )?) + .build(); + Ok(Some(request)) + } + + fn handle_response( + &self, + data: &types::RefundsRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult, errors::ConnectorError> { + let response: itaubank::RefundResponse = res + .response + .parse_struct("itaubank RefundResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration for Itaubank { + fn get_headers( + &self, + req: &types::RefundSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::RefundSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into()) + } + + fn build_request( + &self, + req: &types::RefundSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Get) + .url(&types::RefundSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::RefundSyncType::get_headers(self, req, connectors)?) + .set_body(types::RefundSyncType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::RefundSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: itaubank::RefundResponse = res + .response + .parse_struct("itaubank RefundSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +#[async_trait::async_trait] +impl api::IncomingWebhook for Itaubank { + fn get_webhook_object_reference_id( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_event_type( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } + + fn get_webhook_resource_object( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + Err(report!(errors::ConnectorError::WebhooksNotImplemented)) + } +} diff --git a/crates/router/src/connector/itaubank/transformers.rs b/crates/router/src/connector/itaubank/transformers.rs new file mode 100644 index 000000000000..d9834c57f66c --- /dev/null +++ b/crates/router/src/connector/itaubank/transformers.rs @@ -0,0 +1,216 @@ +use common_utils::types::StringMinorUnit; +use masking::Secret; +use serde::{Deserialize, Serialize}; + +use crate::{ + connector::utils::PaymentsAuthorizeRequestData, + core::errors, + types::{self, api, domain, storage::enums}, +}; + +pub struct ItaubankRouterData { + pub amount: StringMinorUnit, + pub router_data: T, +} + +impl From<(StringMinorUnit, T)> for ItaubankRouterData { + fn from((amount, item): (StringMinorUnit, T)) -> Self { + Self { + amount, + router_data: item, + } + } +} + +#[derive(Default, Debug, Serialize, PartialEq)] +pub struct ItaubankPaymentsRequest { + amount: StringMinorUnit, + card: ItaubankCard, +} + +#[derive(Default, Debug, Serialize, Eq, PartialEq)] +pub struct ItaubankCard { + number: cards::CardNumber, + expiry_month: Secret, + expiry_year: Secret, + cvc: Secret, + complete: bool, +} + +impl TryFrom<&ItaubankRouterData<&types::PaymentsAuthorizeRouterData>> for ItaubankPaymentsRequest { + type Error = error_stack::Report; + fn try_from( + item: &ItaubankRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { + match item.router_data.request.payment_method_data.clone() { + domain::PaymentMethodData::Card(req_card) => { + let card = ItaubankCard { + number: req_card.card_number, + expiry_month: req_card.card_exp_month, + expiry_year: req_card.card_exp_year, + cvc: req_card.card_cvc, + complete: item.router_data.request.is_auto_capture()?, + }; + Ok(Self { + amount: item.amount.clone(), + card, + }) + } + _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } + } +} + +pub struct ItaubankAuthType { + pub(super) api_key: Secret, +} + +impl TryFrom<&types::ConnectorAuthType> for ItaubankAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + match auth_type { + types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self { + api_key: api_key.to_owned(), + }), + _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + } + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "lowercase")] +pub enum ItaubankPaymentStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::AttemptStatus { + fn from(item: ItaubankPaymentStatus) -> Self { + match item { + ItaubankPaymentStatus::Succeeded => Self::Charged, + ItaubankPaymentStatus::Failed => Self::Failure, + ItaubankPaymentStatus::Processing => Self::Authorizing, + } + } +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct ItaubankPaymentsResponse { + status: ItaubankPaymentStatus, + id: String, +} + +impl + TryFrom> + for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData< + F, + ItaubankPaymentsResponse, + T, + types::PaymentsResponseData, + >, + ) -> Result { + Ok(Self { + status: enums::AttemptStatus::from(item.response.status), + response: Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: None, + incremental_authorization_allowed: None, + charge_id: None, + }), + ..item.data + }) + } +} + +#[derive(Default, Debug, Serialize)] +pub struct ItaubankRefundRequest { + pub amount: StringMinorUnit, +} + +impl TryFrom<&ItaubankRouterData<&types::RefundsRouterData>> for ItaubankRefundRequest { + type Error = error_stack::Report; + fn try_from( + item: &ItaubankRouterData<&types::RefundsRouterData>, + ) -> Result { + Ok(Self { + amount: item.amount.to_owned(), + }) + } +} + +#[allow(dead_code)] +#[derive(Debug, Serialize, Default, Deserialize, Clone)] +pub enum RefundStatus { + Succeeded, + Failed, + #[default] + Processing, +} + +impl From for enums::RefundStatus { + fn from(item: RefundStatus) -> Self { + match item { + RefundStatus::Succeeded => Self::Success, + RefundStatus::Failed => Self::Failure, + RefundStatus::Processing => Self::Pending, + } + } +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct RefundResponse { + id: String, + status: RefundStatus, +} + +impl TryFrom> + for types::RefundsRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(types::RefundsResponseData { + connector_refund_id: item.response.id.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +impl TryFrom> + for types::RefundsRouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::RefundsResponseRouterData, + ) -> Result { + Ok(Self { + response: Ok(types::RefundsResponseData { + connector_refund_id: item.response.id.to_string(), + refund_status: enums::RefundStatus::from(item.response.status), + }), + ..item.data + }) + } +} + +#[derive(Default, Debug, Serialize, Deserialize, PartialEq)] +pub struct ItaubankErrorResponse { + pub status_code: u16, + pub code: String, + pub message: String, + pub reason: Option, +} diff --git a/crates/router/src/core/payments/connector_integration_v2_impls.rs b/crates/router/src/core/payments/connector_integration_v2_impls.rs index fbedca14aeba..5220b3699b27 100644 --- a/crates/router/src/core/payments/connector_integration_v2_impls.rs +++ b/crates/router/src/core/payments/connector_integration_v2_impls.rs @@ -663,6 +663,7 @@ default_imp_for_new_connector_integration_payment!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -747,6 +748,7 @@ default_imp_for_new_connector_integration_refund!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -826,6 +828,7 @@ default_imp_for_new_connector_integration_connector_access_token!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -927,6 +930,7 @@ default_imp_for_new_connector_integration_accept_dispute!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1010,6 +1014,7 @@ default_imp_for_new_connector_integration_defend_dispute!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1077,6 +1082,7 @@ default_imp_for_new_connector_integration_submit_evidence!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1171,6 +1177,7 @@ default_imp_for_new_connector_integration_file_upload!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1247,6 +1254,7 @@ default_imp_for_new_connector_integration_payouts!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1333,6 +1341,7 @@ default_imp_for_new_connector_integration_payouts_create!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1419,6 +1428,7 @@ default_imp_for_new_connector_integration_payouts_eligibility!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1505,6 +1515,7 @@ default_imp_for_new_connector_integration_payouts_fulfill!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1591,6 +1602,7 @@ default_imp_for_new_connector_integration_payouts_cancel!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1677,6 +1689,7 @@ default_imp_for_new_connector_integration_payouts_quote!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1763,6 +1776,7 @@ default_imp_for_new_connector_integration_payouts_recipient!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1849,6 +1863,7 @@ default_imp_for_new_connector_integration_payouts_sync!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1935,6 +1950,7 @@ default_imp_for_new_connector_integration_payouts_recipient_account!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2019,6 +2035,7 @@ default_imp_for_new_connector_integration_webhook_source_verification!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2095,6 +2112,7 @@ default_imp_for_new_connector_integration_frm!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2181,6 +2199,7 @@ default_imp_for_new_connector_integration_frm_sale!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2267,6 +2286,7 @@ default_imp_for_new_connector_integration_frm_checkout!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2353,6 +2373,7 @@ default_imp_for_new_connector_integration_frm_transaction!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2439,6 +2460,7 @@ default_imp_for_new_connector_integration_frm_fulfillment!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2525,6 +2547,7 @@ default_imp_for_new_connector_integration_frm_record_return!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2608,6 +2631,7 @@ default_imp_for_new_connector_integration_revoking_mandates!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2719,6 +2743,7 @@ default_imp_for_new_connector_integration_connector_authentication!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 53ca8d02451e..57ad68057ff3 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -208,6 +208,7 @@ default_imp_for_complete_authorize!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Multisafepay, @@ -293,6 +294,7 @@ default_imp_for_webhook_source_verification!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -386,6 +388,7 @@ default_imp_for_create_customer!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -474,6 +477,7 @@ default_imp_for_connector_redirect_response!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Multisafepay, @@ -544,6 +548,7 @@ default_imp_for_connector_request_id!( connector::Gocardless, connector::Gpayments, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -639,6 +644,7 @@ default_imp_for_accept_dispute!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -755,6 +761,7 @@ default_imp_for_file_upload!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -848,6 +855,7 @@ default_imp_for_submit_evidence!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -941,6 +949,7 @@ default_imp_for_defend_dispute!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1041,6 +1050,7 @@ default_imp_for_pre_processing_steps!( connector::Dlocal, connector::Ebanx, connector::Iatapay, + connector::Itaubank, connector::Fiserv, connector::Forte, connector::Globalpay, @@ -1121,6 +1131,7 @@ default_imp_for_post_processing_steps!( connector::Dlocal, connector::Ebanx, connector::Iatapay, + connector::Itaubank, connector::Fiserv, connector::Forte, connector::Globalpay, @@ -1195,6 +1206,7 @@ default_imp_for_payouts!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1285,6 +1297,7 @@ default_imp_for_payouts_create!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1378,6 +1391,7 @@ default_imp_for_payouts_retrieve!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1474,6 +1488,7 @@ default_imp_for_payouts_eligibility!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1565,6 +1580,7 @@ default_imp_for_payouts_fulfill!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1655,6 +1671,7 @@ default_imp_for_payouts_cancel!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1748,6 +1765,7 @@ default_imp_for_payouts_quote!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1842,6 +1860,7 @@ default_imp_for_payouts_recipient!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -1939,6 +1958,7 @@ default_imp_for_payouts_recipient_account!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2033,6 +2053,7 @@ default_imp_for_approve!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2128,6 +2149,7 @@ default_imp_for_reject!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2207,6 +2229,7 @@ default_imp_for_fraud_check!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2302,6 +2325,7 @@ default_imp_for_frm_sale!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2397,6 +2421,7 @@ default_imp_for_frm_checkout!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2492,6 +2517,7 @@ default_imp_for_frm_transaction!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2587,6 +2613,7 @@ default_imp_for_frm_fulfillment!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2682,6 +2709,7 @@ default_imp_for_frm_record_return!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2774,6 +2802,7 @@ default_imp_for_incremental_authorization!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -2866,6 +2895,7 @@ default_imp_for_revoking_mandates!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -3018,6 +3048,7 @@ default_imp_for_connector_authentication!( connector::Gocardless, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, @@ -3108,6 +3139,7 @@ default_imp_for_authorize_session_token!( connector::Gpayments, connector::Helcim, connector::Iatapay, + connector::Itaubank, connector::Klarna, connector::Mifinity, connector::Mollie, diff --git a/crates/router/tests/connectors/itaubank.rs b/crates/router/tests/connectors/itaubank.rs new file mode 100644 index 000000000000..4d47f566c009 --- /dev/null +++ b/crates/router/tests/connectors/itaubank.rs @@ -0,0 +1,420 @@ +use masking::Secret; +use router::types::{self, api, domain, storage::enums}; +use test_utils::connector_auth; + +use crate::utils::{self, ConnectorActions}; + +#[derive(Clone, Copy)] +struct ItaubankTest; +impl ConnectorActions for ItaubankTest {} +impl utils::Connector for ItaubankTest { + fn get_data(&self) -> api::ConnectorData { + use router::connector::Adyen; + utils::construct_connector_data_old( + Box::new(Adyen::new()), + types::Connector::Adyen, + api::GetToken::Connector, + None, + ) + } + + fn get_auth_token(&self) -> types::ConnectorAuthType { + utils::to_connector_auth_type( + connector_auth::ConnectorAuthentication::new() + .itaubank + .expect("Missing connector authentication configuration") + .into(), + ) + } + + fn get_name(&self) -> String { + "itaubank".to_string() + } +} + +static CONNECTOR: ItaubankTest = ItaubankTest {}; + +fn get_default_payment_info() -> Option { + None +} + +fn payment_method_details() -> Option { + None +} + +// Cards Positive Tests +// Creates a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_only_authorize_payment() { + let response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized); +} + +// Captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info()) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Partially captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment( + payment_method_details(), + Some(types::PaymentsCaptureData { + amount_to_capture: 50, + ..utils::PaymentCaptureType::default().0 + }), + get_default_payment_info(), + ) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_authorized_payment() { + let authorize_response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Authorized, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("PSync response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized,); +} + +// Voids a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_void_authorized_payment() { + let response = CONNECTOR + .authorize_and_void_payment( + payment_method_details(), + Some(types::PaymentsCancelData { + connector_transaction_id: String::from(""), + cancellation_reason: Some("requested_by_customer".to_string()), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("Void payment response"); + assert_eq!(response.status, enums::AttemptStatus::Voided); +} + +// Refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Synchronizes a refund using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_manually_captured_refund() { + let refund_response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_make_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_auto_captured_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Charged, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + capture_method: Some(enums::CaptureMethod::Automatic), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!(response.status, enums::AttemptStatus::Charged,); +} + +// Refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_auto_captured_payment() { + let response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_succeeded_payment() { + let refund_response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + refund_response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_succeeded_payment_multiple_times() { + CONNECTOR + .make_payment_and_multiple_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await; +} + +// Synchronizes a refund using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_refund() { + let refund_response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Cards Negative scenarios +// Creates a payment with incorrect CVC. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_cvc() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_cvc: Secret::new("12345".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's security code is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry month. +#[actix_web::test] +async fn should_fail_payment_for_invalid_exp_month() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_month: Secret::new("20".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration month is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry year. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_expiry_year() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_year: Secret::new("2000".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration year is invalid.".to_string(), + ); +} + +// Voids a payment using automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_fail_void_payment_for_auto_capture() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let void_response = CONNECTOR + .void_payment(txn_id.unwrap(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + void_response.response.unwrap_err().message, + "You cannot cancel this PaymentIntent because it has a status of succeeded." + ); +} + +// Captures a payment using invalid connector payment id. +#[actix_web::test] +async fn should_fail_capture_for_invalid_payment() { + let capture_response = CONNECTOR + .capture_payment("123456789".to_string(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + capture_response.response.unwrap_err().message, + String::from("No such payment_intent: '123456789'") + ); +} + +// Refunds a payment with refund amount higher than payment amount. +#[actix_web::test] +async fn should_fail_for_refund_amount_higher_than_payment_amount() { + let response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 150, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Refund amount (₹1.50) is greater than charge amount (₹1.00)", + ); +} + +// Connector dependent test cases goes here + +// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests diff --git a/crates/router/tests/connectors/main.rs b/crates/router/tests/connectors/main.rs index 0283b6d2844e..5fb2f5f004e5 100644 --- a/crates/router/tests/connectors/main.rs +++ b/crates/router/tests/connectors/main.rs @@ -38,6 +38,7 @@ mod gocardless; mod gpayments; mod helcim; mod iatapay; +mod itaubank; mod mifinity; mod mollie; mod multisafepay; diff --git a/crates/router/tests/connectors/sample_auth.toml b/crates/router/tests/connectors/sample_auth.toml index cd4f854fa17d..443034b2736a 100644 --- a/crates/router/tests/connectors/sample_auth.toml +++ b/crates/router/tests/connectors/sample_auth.toml @@ -211,10 +211,6 @@ api_key="API Key" certificate="Certificate" private_key="Private Key" - -[ebanx] -api_key="API Key" - [zsl] api_key= "Key" key1= "Merchant id" @@ -246,3 +242,6 @@ api_key="API Key" key1 = "Merchant id" api_secret = "Razorpay Id" key2 = "Razorpay Secret" + +[itaubank] +api_key="API Key" \ No newline at end of file diff --git a/crates/test_utils/src/connector_auth.rs b/crates/test_utils/src/connector_auth.rs index e26b234e9d60..56058d787498 100644 --- a/crates/test_utils/src/connector_auth.rs +++ b/crates/test_utils/src/connector_auth.rs @@ -43,6 +43,7 @@ pub struct ConnectorAuthentication { pub gpayments: Option, pub helcim: Option, pub iatapay: Option, + pub itaubank: Option, pub mifinity: Option, pub mollie: Option, pub multisafepay: Option, diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 35ff07f1b8e0..2daeecf2854a 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -107,6 +107,7 @@ gocardless.base_url = "https://api-sandbox.gocardless.com" gpayments.base_url = "https://{{merchant_endpoint_prefix}}-test.api.as1.gpayments.net" helcim.base_url = "https://api.helcim.com/" iatapay.base_url = "https://sandbox.iata-pay.iata.org/api/v1" +itaubank.base_url = "https://sandbox.devportal.itau.com.br/itau-ep9-gtw-pix-recebimentos-ext-v2/v2" klarna.base_url = "https://api{{klarna_region}}.playground.klarna.com/" mifinity.base_url = "https://demo.mifinity.com/" mollie.base_url = "https://api.mollie.com/v2/" @@ -187,6 +188,7 @@ cards = [ "gpayments", "helcim", "iatapay", + "itaubank", "mollie", "multisafepay", "netcetera", diff --git a/scripts/add_connector.sh b/scripts/add_connector.sh index 390654d5677c..87822b2d1c62 100755 --- a/scripts/add_connector.sh +++ b/scripts/add_connector.sh @@ -6,7 +6,7 @@ function find_prev_connector() { git checkout $self cp $self $self.tmp # Add new connector to existing list and sort it - connectors=(aci adyen adyenplatform airwallex applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless gpayments helcim iatapay klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode payeezy payme payone paypal payu placetopay powertranz prophetpay rapyd razorpay shift4 square stax stripe threedsecureio trustpay tsys volt wise worldline worldpay zsl "$1") + connectors=(aci adyen adyenplatform airwallex applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless gpayments helcim iatapay itaubank klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode payeezy payme payone paypal payu placetopay powertranz prophetpay rapyd razorpay shift4 square stax stripe threedsecureio trustpay tsys volt wise worldline worldpay zsl "$1") IFS=$'\n' sorted=($(sort <<<"${connectors[*]}")); unset IFS res=`echo ${sorted[@]}` sed -i'' -e "s/^ connectors=.*/ connectors=($res \"\$1\")/" $self.tmp From 6d74527f4428c8cb61e89d59385bfb2dd8056c2e Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:01:29 +0530 Subject: [PATCH 11/26] refactor(router): remove id dependency from merchant connector account, dispute and mandate (#5330) Co-authored-by: hrithikesh026 Co-authored-by: sai-harsha-vardhan --- .../src/query/merchant_connector_account.rs | 2 +- crates/diesel_models/src/schema.rs | 6 +++--- crates/diesel_models/src/schema_v2.rs | 6 +++--- .../down.sql | 3 +++ .../up.sql | 11 +++++++++++ .../down.sql | 3 +++ .../up.sql | 11 +++++++++++ .../down.sql | 5 +++++ .../up.sql | 12 ++++++++++++ 9 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 migrations/2024-07-15-120121_change_primary_key_for_dispute/down.sql create mode 100644 migrations/2024-07-15-120121_change_primary_key_for_dispute/up.sql create mode 100644 migrations/2024-07-15-143920_change_primary_key_for_mandate/down.sql create mode 100644 migrations/2024-07-15-143920_change_primary_key_for_mandate/up.sql create mode 100644 migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/down.sql create mode 100644 migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/up.sql diff --git a/crates/diesel_models/src/query/merchant_connector_account.rs b/crates/diesel_models/src/query/merchant_connector_account.rs index bd24d12ec22c..d275211ebde4 100644 --- a/crates/diesel_models/src/query/merchant_connector_account.rs +++ b/crates/diesel_models/src/query/merchant_connector_account.rs @@ -25,7 +25,7 @@ impl MerchantConnectorAccount { ) -> StorageResult { match generics::generic_update_by_id::<::Table, _, _, _>( conn, - self.id, + self.merchant_connector_id.to_owned(), merchant_connector_account, ) .await diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index a5185dc6076a..68689e864407 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -336,7 +336,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - dispute (id) { + dispute (dispute_id) { id -> Int4, #[max_length = 64] dispute_id -> Varchar, @@ -587,7 +587,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - mandate (id) { + mandate (mandate_id) { id -> Int4, #[max_length = 64] mandate_id -> Varchar, @@ -677,7 +677,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - merchant_connector_account (id) { + merchant_connector_account (merchant_connector_id) { id -> Int4, #[max_length = 64] merchant_id -> Varchar, diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index da8729cf8357..e353cbd5666b 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -336,7 +336,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - dispute (id) { + dispute (dispute_id) { id -> Int4, #[max_length = 64] dispute_id -> Varchar, @@ -587,7 +587,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - mandate (id) { + mandate (mandate_id) { id -> Int4, #[max_length = 64] mandate_id -> Varchar, @@ -677,7 +677,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - merchant_connector_account (id) { + merchant_connector_account (merchant_connector_id) { id -> Int4, #[max_length = 64] merchant_id -> Varchar, diff --git a/migrations/2024-07-15-120121_change_primary_key_for_dispute/down.sql b/migrations/2024-07-15-120121_change_primary_key_for_dispute/down.sql new file mode 100644 index 000000000000..ff4e1664cb28 --- /dev/null +++ b/migrations/2024-07-15-120121_change_primary_key_for_dispute/down.sql @@ -0,0 +1,3 @@ +ALTER TABLE dispute DROP CONSTRAINT dispute_pkey; + +ALTER TABLE dispute ADD PRIMARY KEY (id); \ No newline at end of file diff --git a/migrations/2024-07-15-120121_change_primary_key_for_dispute/up.sql b/migrations/2024-07-15-120121_change_primary_key_for_dispute/up.sql new file mode 100644 index 000000000000..794daa8850b9 --- /dev/null +++ b/migrations/2024-07-15-120121_change_primary_key_for_dispute/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here +-- The below query will lock the dispute table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query is necessary for the application to not use id in update of dispute +-- This query should be run only after the new version of application is deployed +ALTER TABLE dispute DROP CONSTRAINT dispute_pkey; + +-- Use the `dispute_id` column as primary key +ALTER TABLE dispute +ADD PRIMARY KEY (dispute_id); diff --git a/migrations/2024-07-15-143920_change_primary_key_for_mandate/down.sql b/migrations/2024-07-15-143920_change_primary_key_for_mandate/down.sql new file mode 100644 index 000000000000..b04e648e07aa --- /dev/null +++ b/migrations/2024-07-15-143920_change_primary_key_for_mandate/down.sql @@ -0,0 +1,3 @@ +ALTER TABLE mandate DROP CONSTRAINT mandate_pkey; + +ALTER TABLE mandate ADD PRIMARY KEY (id); \ No newline at end of file diff --git a/migrations/2024-07-15-143920_change_primary_key_for_mandate/up.sql b/migrations/2024-07-15-143920_change_primary_key_for_mandate/up.sql new file mode 100644 index 000000000000..13ff09a17e49 --- /dev/null +++ b/migrations/2024-07-15-143920_change_primary_key_for_mandate/up.sql @@ -0,0 +1,11 @@ +-- Your SQL goes here +-- The below query will lock the mandate table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query is necessary for the application to not use id in update of mandate +-- This query should be run only after the new version of application is deployed +ALTER TABLE mandate DROP CONSTRAINT mandate_pkey; + +-- Use the `mandate_id` column as primary key +ALTER TABLE mandate +ADD PRIMARY KEY (mandate_id); diff --git a/migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/down.sql b/migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/down.sql new file mode 100644 index 000000000000..2107fd6370f9 --- /dev/null +++ b/migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_connector_account DROP CONSTRAINT merchant_connector_account_pkey; + +ALTER TABLE merchant_connector_account +ADD PRIMARY KEY (id); diff --git a/migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/up.sql b/migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/up.sql new file mode 100644 index 000000000000..c12fc937bb04 --- /dev/null +++ b/migrations/2024-07-15-170210_change_primary_key_for_merchant_connector_account/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here +-- The below query will lock the merchant connector account table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query should be run only after the new version of application is deployed +ALTER TABLE merchant_connector_account DROP CONSTRAINT merchant_connector_account_pkey; + +-- Use the `merchant_connector_id` column as primary key +-- This is not a unique column, but in an ideal scenario there should not be any duplicate keys as this is being generated by the application +-- So this query should not fail for not null or duplicate values reasons +ALTER TABLE merchant_connector_account +ADD PRIMARY KEY (merchant_connector_id); From a0c367ee8c7f04cfdf1f9d4863447f24279a537f Mon Sep 17 00:00:00 2001 From: Narayan Bhat <48803246+Narayanbhat166@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:10:57 +0530 Subject: [PATCH 12/26] refactor(merchant_account): change primary key for merchant account (#5327) Co-authored-by: hrithikesh026 --- crates/diesel_models/src/query/merchant_account.rs | 2 +- crates/diesel_models/src/schema.rs | 2 +- crates/diesel_models/src/schema_v2.rs | 2 +- .../down.sql | 5 +++++ .../up.sql | 13 +++++++++++++ 5 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 migrations/2024-07-15-111327_change_primary_key_for_merchant_account/down.sql create mode 100644 migrations/2024-07-15-111327_change_primary_key_for_merchant_account/up.sql diff --git a/crates/diesel_models/src/query/merchant_account.rs b/crates/diesel_models/src/query/merchant_account.rs index dd2f284305be..66526e11cb2d 100644 --- a/crates/diesel_models/src/query/merchant_account.rs +++ b/crates/diesel_models/src/query/merchant_account.rs @@ -22,7 +22,7 @@ impl MerchantAccount { ) -> StorageResult { match generics::generic_update_by_id::<::Table, _, _, _>( conn, - self.id, + self.merchant_id.clone(), merchant_account, ) .await diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 68689e864407..b27d1e9611fd 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -633,7 +633,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - merchant_account (id) { + merchant_account (merchant_id) { id -> Int4, #[max_length = 64] merchant_id -> Varchar, diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index e353cbd5666b..3fdbf45025fc 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -633,7 +633,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - merchant_account (id) { + merchant_account (merchant_id) { id -> Int4, #[max_length = 64] merchant_id -> Varchar, diff --git a/migrations/2024-07-15-111327_change_primary_key_for_merchant_account/down.sql b/migrations/2024-07-15-111327_change_primary_key_for_merchant_account/down.sql new file mode 100644 index 000000000000..2f169d80194d --- /dev/null +++ b/migrations/2024-07-15-111327_change_primary_key_for_merchant_account/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE merchant_account DROP CONSTRAINT merchant_account_pkey; + +ALTER TABLE merchant_account +ADD PRIMARY KEY (id); diff --git a/migrations/2024-07-15-111327_change_primary_key_for_merchant_account/up.sql b/migrations/2024-07-15-111327_change_primary_key_for_merchant_account/up.sql new file mode 100644 index 000000000000..5db5076c07ae --- /dev/null +++ b/migrations/2024-07-15-111327_change_primary_key_for_merchant_account/up.sql @@ -0,0 +1,13 @@ +-- Your SQL goes here +-- The below query will lock the merchant account table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query is necessary for the application to not use id in update of merchant_account +-- This query should be run after the new version of application is deployed +ALTER TABLE merchant_account DROP CONSTRAINT merchant_account_pkey; + +-- Use the `merchant_id` column as primary key +-- This is already a unique, not null column +-- So this query should not fail for not null or duplicate values reasons +ALTER TABLE merchant_account +ADD PRIMARY KEY (merchant_id); From 78a7804b9c8b4db881b112fc72e31cfd3e97a82d Mon Sep 17 00:00:00 2001 From: Amisha Prabhat <55580080+Aprabhat19@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:13:38 +0530 Subject: [PATCH 13/26] refactor(routing): Remove backwards compatibility for the routing crate (#3015) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> Co-authored-by: Shanks --- crates/api_models/Cargo.toml | 6 +- crates/api_models/src/events/routing.rs | 6 +- crates/api_models/src/routing.rs | 108 +-- crates/euclid/Cargo.toml | 1 - crates/euclid/src/frontend/ast.rs | 10 +- crates/euclid/src/frontend/dir.rs | 54 +- crates/euclid_wasm/Cargo.toml | 6 +- crates/euclid_wasm/src/lib.rs | 2 - crates/kgraph_utils/Cargo.toml | 2 +- crates/kgraph_utils/src/mca.rs | 6 +- crates/router/Cargo.toml | 8 +- .../stripe/payment_intents/types.rs | 4 - .../stripe/setup_intents/types.rs | 4 - crates/router/src/core/admin.rs | 4 - .../router/src/core/payment_methods/cards.rs | 28 +- .../router/src/core/payment_methods/utils.rs | 24 +- crates/router/src/core/payments.rs | 146 +-- crates/router/src/core/payments/helpers.rs | 2 +- crates/router/src/core/payments/routing.rs | 161 +--- .../src/core/payments/routing/transformers.rs | 4 - crates/router/src/core/payouts.rs | 6 - crates/router/src/core/payouts/helpers.rs | 28 +- crates/router/src/core/routing.rs | 864 +++++------------- crates/router/src/core/routing/helpers.rs | 22 +- .../router/src/core/routing/transformers.rs | 6 +- crates/router/src/db/merchant_account.rs | 6 - crates/router/src/routes/app.rs | 44 +- crates/router/src/routes/routing.rs | 254 ++--- crates/router/src/types/api/routing.rs | 10 +- crates/router/src/types/storage.rs | 5 +- .../Payments - Create/request.json | 4 - .../QuickStart/Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../QuickStart/Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Save card payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../QuickStart/Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../QuickStart/Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 10 +- .../Payments - Create/request.json | 6 +- .../QuickStart/Payments - Create/request.json | 10 +- .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Recurring Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Recurring Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Save card payments - Create/request.json | 12 +- .../Payments - Create/request.json | 4 - .../Save card payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Save card payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 12 +- .../QuickStart/Payments - Create/request.json | 4 - .../request.json | 4 - .../request.json | 4 - .../request.json | 4 - .../request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Recurring Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create Again/request.json | 4 - .../Payments - Create Yet Again/request.json | 4 - .../Payments/Payments - Create/request.json | 4 - .../Payments - Create-copy/request.json | 5 +- .../QuickStart/Payments - Create/request.json | 5 +- .../Refunds/Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../Payments - Create/request.json | 4 - .../QuickStart/Payments - Create/request.json | 4 - 122 files changed, 469 insertions(+), 1762 deletions(-) diff --git a/crates/api_models/Cargo.toml b/crates/api_models/Cargo.toml index 9df13c85a870..0595bdc3e75d 100644 --- a/crates/api_models/Cargo.toml +++ b/crates/api_models/Cargo.toml @@ -9,17 +9,13 @@ license.workspace = true [features] default = [] -business_profile_routing = [] -connector_choice_bcompat = [] errors = ["dep:actix-web", "dep:reqwest"] -backwards_compatibility = ["connector_choice_bcompat"] -connector_choice_mca_id = ["euclid/connector_choice_mca_id"] dummy_connector = ["euclid/dummy_connector", "common_enums/dummy_connector"] detailed_errors = [] payouts = [] frm = [] olap = [] -openapi = ["common_enums/openapi", "olap", "backwards_compatibility", "business_profile_routing", "connector_choice_mca_id", "recon", "dummy_connector", "olap"] +openapi = ["common_enums/openapi", "olap", "recon", "dummy_connector", "olap"] recon = [] v2 = [] diff --git a/crates/api_models/src/events/routing.rs b/crates/api_models/src/events/routing.rs index a09735bc5722..1f068678c598 100644 --- a/crates/api_models/src/events/routing.rs +++ b/crates/api_models/src/events/routing.rs @@ -3,10 +3,8 @@ use common_utils::events::{ApiEventMetric, ApiEventsType}; use crate::routing::{ LinkedRoutingConfigRetrieveResponse, MerchantRoutingAlgorithm, ProfileDefaultRoutingConfig, RoutingAlgorithmId, RoutingConfigRequest, RoutingDictionaryRecord, RoutingKind, - RoutingPayloadWrapper, + RoutingPayloadWrapper, RoutingRetrieveLinkQuery, RoutingRetrieveQuery, }; -#[cfg(feature = "business_profile_routing")] -use crate::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}; impl ApiEventMetric for RoutingKind { fn get_api_event_type(&self) -> Option { @@ -49,7 +47,6 @@ impl ApiEventMetric for ProfileDefaultRoutingConfig { } } -#[cfg(feature = "business_profile_routing")] impl ApiEventMetric for RoutingRetrieveQuery { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Routing) @@ -62,7 +59,6 @@ impl ApiEventMetric for RoutingConfigRequest { } } -#[cfg(feature = "business_profile_routing")] impl ApiEventMetric for RoutingRetrieveLinkQuery { fn get_api_event_type(&self) -> Option { Some(ApiEventsType::Routing) diff --git a/crates/api_models/src/routing.rs b/crates/api_models/src/routing.rs index 66f3ff558ea4..4e62a5f67d29 100644 --- a/crates/api_models/src/routing.rs +++ b/crates/api_models/src/routing.rs @@ -45,7 +45,6 @@ pub struct ProfileDefaultRoutingConfig { pub connectors: Vec, } -#[cfg(feature = "business_profile_routing")] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct RoutingRetrieveQuery { pub limit: Option, @@ -54,7 +53,6 @@ pub struct RoutingRetrieveQuery { pub profile_id: Option, } -#[cfg(feature = "business_profile_routing")] #[derive(Debug, serde::Deserialize, serde::Serialize)] pub struct RoutingRetrieveLinkQuery { pub profile_id: Option, @@ -77,7 +75,6 @@ pub enum LinkedRoutingConfigRetrieveResponse { /// Routing Algorithm specific to merchants pub struct MerchantRoutingAlgorithm { pub id: String, - #[cfg(feature = "business_profile_routing")] pub profile_id: String, pub name: String, pub description: String, @@ -129,27 +126,17 @@ impl EuclidAnalysable for ConnectorSelection { .into_iter() .map(|connector_choice| { let connector_name = connector_choice.connector.to_string(); - #[cfg(not(feature = "connector_choice_mca_id"))] - let sub_label = connector_choice.sub_label.clone(); - #[cfg(feature = "connector_choice_mca_id")] let mca_id = connector_choice.merchant_connector_id.clone(); ( euclid::frontend::dir::DirValue::Connector(Box::new(connector_choice.into())), std::collections::HashMap::from_iter([( "CONNECTOR_SELECTION".to_string(), - #[cfg(feature = "connector_choice_mca_id")] serde_json::json!({ "rule_name": rule_name, "connector_name": connector_name, "mca_id": mca_id, }), - #[cfg(not(feature = "connector_choice_mca_id"))] - serde_json ::json!({ - "rule_name": rule_name, - "connector_name": connector_name, - "sub_label": sub_label, - }), )]), ) }) @@ -163,143 +150,90 @@ pub struct ConnectorVolumeSplit { pub split: u8, } -#[cfg(feature = "connector_choice_bcompat")] +/// Routable Connector chosen for a payment +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] +#[serde(from = "RoutableChoiceSerde", into = "RoutableChoiceSerde")] +pub struct RoutableConnectorChoice { + #[serde(skip)] + pub choice_kind: RoutableChoiceKind, + pub connector: RoutableConnectors, + pub merchant_connector_id: Option, +} + #[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema)] pub enum RoutableChoiceKind { OnlyConnector, FullStruct, } -#[cfg(feature = "connector_choice_bcompat")] #[derive(Debug, serde::Deserialize, serde::Serialize)] #[serde(untagged)] pub enum RoutableChoiceSerde { OnlyConnector(Box), FullStruct { connector: RoutableConnectors, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: Option, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: Option, }, } -#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)] -#[cfg_attr( - feature = "connector_choice_bcompat", - serde(from = "RoutableChoiceSerde"), - serde(into = "RoutableChoiceSerde") -)] -#[cfg_attr(not(feature = "connector_choice_bcompat"), derive(PartialEq, Eq))] - -/// Routable Connector chosen for a payment -pub struct RoutableConnectorChoice { - #[cfg(feature = "connector_choice_bcompat")] - #[serde(skip)] - pub choice_kind: RoutableChoiceKind, - pub connector: RoutableConnectors, - #[cfg(feature = "connector_choice_mca_id")] - pub merchant_connector_id: Option, - #[cfg(not(feature = "connector_choice_mca_id"))] - pub sub_label: Option, -} - impl std::fmt::Display for RoutableConnectorChoice { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - #[cfg(feature = "connector_choice_mca_id")] let base = self.connector.to_string(); - #[cfg(not(feature = "connector_choice_mca_id"))] - let base = { - let mut sub_base = self.connector.to_string(); - if let Some(ref label) = self.sub_label { - sub_base.push('_'); - sub_base.push_str(label); - } - - sub_base - }; - write!(f, "{}", base) } } -#[cfg(feature = "connector_choice_bcompat")] -impl PartialEq for RoutableConnectorChoice { - fn eq(&self, other: &Self) -> bool { - #[cfg(not(feature = "connector_choice_mca_id"))] - { - self.connector.eq(&other.connector) && self.sub_label.eq(&other.sub_label) +impl From for ast::ConnectorChoice { + fn from(value: RoutableConnectorChoice) -> Self { + Self { + connector: value.connector, } + } +} - #[cfg(feature = "connector_choice_mca_id")] - { - self.connector.eq(&other.connector) - && self.merchant_connector_id.eq(&other.merchant_connector_id) - } +impl PartialEq for RoutableConnectorChoice { + fn eq(&self, other: &Self) -> bool { + self.connector.eq(&other.connector) + && self.merchant_connector_id.eq(&other.merchant_connector_id) } } -#[cfg(feature = "connector_choice_bcompat")] impl Eq for RoutableConnectorChoice {} -#[cfg(feature = "connector_choice_bcompat")] impl From for RoutableConnectorChoice { fn from(value: RoutableChoiceSerde) -> Self { match value { RoutableChoiceSerde::OnlyConnector(connector) => Self { choice_kind: RoutableChoiceKind::OnlyConnector, connector: *connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: None, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, }, RoutableChoiceSerde::FullStruct { connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label, } => Self { choice_kind: RoutableChoiceKind::FullStruct, connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label, }, } } } -#[cfg(feature = "connector_choice_bcompat")] impl From for RoutableChoiceSerde { fn from(value: RoutableConnectorChoice) -> Self { match value.choice_kind { RoutableChoiceKind::OnlyConnector => Self::OnlyConnector(Box::new(value.connector)), RoutableChoiceKind::FullStruct => Self::FullStruct { connector: value.connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: value.merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: value.sub_label, }, } } } -impl From for ast::ConnectorChoice { - fn from(value: RoutableConnectorChoice) -> Self { - Self { - connector: value.connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: value.sub_label, - } - } -} - #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, strum::Display, ToSchema)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] @@ -499,7 +433,7 @@ impl RoutingAlgorithmRef { pub struct RoutingDictionaryRecord { pub id: String, - #[cfg(feature = "business_profile_routing")] + pub profile_id: String, pub name: String, pub kind: RoutingAlgorithmKind, diff --git a/crates/euclid/Cargo.toml b/crates/euclid/Cargo.toml index 295f0327788d..63c9c1508d93 100644 --- a/crates/euclid/Cargo.toml +++ b/crates/euclid/Cargo.toml @@ -29,7 +29,6 @@ common_utils = { version = "0.1.0", path = "../common_utils"} default = [] ast_parser = ["dep:nom"] valued_jit = [] -connector_choice_mca_id = [] dummy_connector = [] payouts = [] diff --git a/crates/euclid/src/frontend/ast.rs b/crates/euclid/src/frontend/ast.rs index ecb6aa87f27b..c77736effa9b 100644 --- a/crates/euclid/src/frontend/ast.rs +++ b/crates/euclid/src/frontend/ast.rs @@ -12,8 +12,6 @@ use crate::types::{DataType, Metadata}; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct ConnectorChoice { pub connector: RoutableConnectors, - #[cfg(not(feature = "connector_choice_mca_id"))] - pub sub_label: Option, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)] @@ -163,18 +161,16 @@ pub struct Program { #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct RoutableConnectorChoice { - #[cfg(feature = "connector_choice_bcompat")] #[serde(skip)] pub choice_kind: RoutableChoiceKind, - #[cfg(feature = "connector_choice_mca_id")] + pub connector: RoutableConnectors, pub merchant_connector_id: Option, - #[cfg(not(feature = "connector_choice_mca_id"))] - pub sub_label: Option, } -#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)] +#[derive(Debug, Default, Clone, Deserialize, Serialize, ToSchema)] pub enum RoutableChoiceKind { OnlyConnector, + #[default] FullStruct, } diff --git a/crates/euclid/src/frontend/dir.rs b/crates/euclid/src/frontend/dir.rs index 8cfdf6567166..9a7134e708a7 100644 --- a/crates/euclid/src/frontend/dir.rs +++ b/crates/euclid/src/frontend/dir.rs @@ -9,7 +9,6 @@ use strum::IntoEnumIterator; use crate::{enums as euclid_enums, frontend::ast, types}; #[macro_export] -#[cfg(feature = "connector_choice_mca_id")] macro_rules! dirval { (Connector = $name:ident) => { $crate::frontend::dir::DirValue::Connector(Box::new( @@ -46,53 +45,6 @@ macro_rules! dirval { }}; } -#[macro_export] -#[cfg(not(feature = "connector_choice_mca_id"))] -macro_rules! dirval { - (Connector = $name:ident) => { - $crate::frontend::dir::DirValue::Connector(Box::new( - $crate::frontend::ast::ConnectorChoice { - connector: $crate::enums::RoutableConnectors::$name, - sub_label: None, - }, - )) - }; - - (Connector = ($name:ident, $sub_label:literal)) => { - $crate::frontend::dir::DirValue::Connector(Box::new( - $crate::frontend::ast::ConnectorChoice { - connector: $crate::enums::RoutableConnectors::$name, - sub_label: Some($sub_label.to_string()), - }, - )) - }; - - ($key:ident = $val:ident) => {{ - pub use $crate::frontend::dir::enums::*; - - $crate::frontend::dir::DirValue::$key($key::$val) - }}; - - ($key:ident = $num:literal) => {{ - $crate::frontend::dir::DirValue::$key($crate::types::NumValue { - number: common_utils::types::MinorUnit::new($num), - refinement: None, - }) - }}; - - ($key:ident s= $str:literal) => {{ - $crate::frontend::dir::DirValue::$key($crate::types::StrValue { - value: $str.to_string(), - }) - }}; - ($key:literal = $str:literal) => {{ - $crate::frontend::dir::DirValue::MetaData($crate::types::MetadataValue { - key: $key.to_string(), - value: $str.to_string(), - }) - }}; -} - #[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize)] pub struct DirKey { pub kind: DirKeyKind, @@ -482,11 +434,7 @@ impl DirKeyKind { Self::Connector => Some( common_enums::RoutableConnectors::iter() .map(|connector| { - DirValue::Connector(Box::new(ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - })) + DirValue::Connector(Box::new(ast::ConnectorChoice { connector })) }) .collect(), ), diff --git a/crates/euclid_wasm/Cargo.toml b/crates/euclid_wasm/Cargo.toml index 293940f27c4a..be8f61240d66 100644 --- a/crates/euclid_wasm/Cargo.toml +++ b/crates/euclid_wasm/Cargo.toml @@ -11,10 +11,8 @@ license.workspace = true crate-type = ["cdylib"] [features] -default = ["connector_choice_bcompat", "payouts", "connector_choice_mca_id"] -release = ["connector_choice_bcompat", "connector_choice_mca_id", "payouts"] -connector_choice_bcompat = ["api_models/connector_choice_bcompat"] -connector_choice_mca_id = ["api_models/connector_choice_mca_id", "euclid/connector_choice_mca_id", "kgraph_utils/connector_choice_mca_id"] +default = ["payouts"] +release = ["payouts"] dummy_connector = ["kgraph_utils/dummy_connector", "connector_configs/dummy_connector"] production = ["connector_configs/production"] development = ["connector_configs/development"] diff --git a/crates/euclid_wasm/src/lib.rs b/crates/euclid_wasm/src/lib.rs index 031440ae5412..eed5c38af9d4 100644 --- a/crates/euclid_wasm/src/lib.rs +++ b/crates/euclid_wasm/src/lib.rs @@ -84,8 +84,6 @@ pub fn seed_knowledge_graph(mcas: JsValue) -> JsResult { .map(|mca| { Ok::<_, strum::ParseError>(ast::ConnectorChoice { connector: RoutableConnectors::from_str(&mca.connector_name)?, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: mca.business_sub_label.clone(), }) }) .collect::>() diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index 7528461dbc52..6172e75629f6 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -8,7 +8,7 @@ license.workspace = true [features] dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector"] -connector_choice_mca_id = ["api_models/connector_choice_mca_id", "euclid/connector_choice_mca_id"] + [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } diff --git a/crates/kgraph_utils/src/mca.rs b/crates/kgraph_utils/src/mca.rs index 5b31a6be648e..a636c4b3a2a7 100644 --- a/crates/kgraph_utils/src/mca.rs +++ b/crates/kgraph_utils/src/mca.rs @@ -651,11 +651,7 @@ fn compile_merchant_connector_graph( None, ) .map_err(KgraphError::GraphConstructionError)?; - let connector_dir_val = dir::DirValue::Connector(Box::new(ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: mca.business_sub_label.clone(), - })); + let connector_dir_val = dir::DirValue::Connector(Box::new(ast::ConnectorChoice { connector })); let connector_info = "Connector"; let connector_node_id = diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 1212869600bf..438e0dc323d9 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -9,24 +9,20 @@ readme = "README.md" license.workspace = true [features] -default = ["kv_store", "stripe", "oltp", "olap", "backwards_compatibility", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "business_profile_routing", "connector_choice_mca_id", "profile_specific_fallback_routing", "retry", "frm", "tls"] +default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls"] tls = ["actix-web/rustls-0_22"] keymanager_mtls = ["reqwest/rustls-tls","common_utils/keymanager_mtls"] email = ["external_services/email", "scheduler/email", "olap"] keymanager_create = [] frm = ["api_models/frm", "hyperswitch_domain_models/frm"] stripe = ["dep:serde_qs"] -release = ["stripe", "email", "backwards_compatibility", "business_profile_routing", "accounts_cache", "kv_store", "connector_choice_mca_id", "profile_specific_fallback_routing", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3","keymanager_mtls","keymanager_create"] +release = ["stripe", "email", "accounts_cache", "kv_store", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3","keymanager_mtls","keymanager_create"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] oltp = ["storage_impl/oltp"] kv_store = ["scheduler/kv_store"] accounts_cache = [] vergen = ["router_env/vergen"] -backwards_compatibility = ["api_models/backwards_compatibility"] -business_profile_routing = ["api_models/business_profile_routing"] -profile_specific_fallback_routing = [] dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector", "kgraph_utils/dummy_connector"] -connector_choice_mca_id = ["api_models/connector_choice_mca_id", "euclid/connector_choice_mca_id", "kgraph_utils/connector_choice_mca_id"] external_access_dc = ["dummy_connector"] detailed_errors = ["api_models/detailed_errors", "error-stack/serde"] payouts = ["api_models/payouts", "common_enums/payouts", "hyperswitch_domain_models/payouts", "storage_impl/payouts"] diff --git a/crates/router/src/compatibility/stripe/payment_intents/types.rs b/crates/router/src/compatibility/stripe/payment_intents/types.rs index 62cf9416b24b..c06193125606 100644 --- a/crates/router/src/compatibility/stripe/payment_intents/types.rs +++ b/crates/router/src/compatibility/stripe/payment_intents/types.rs @@ -293,13 +293,9 @@ impl TryFrom for payments::PaymentsRequest { .map(|connector| { api_models::routing::RoutingAlgorithm::Single(Box::new( api_models::routing::RoutableConnectorChoice { - #[cfg(feature = "backwards_compatibility")] choice_kind: api_models::routing::RoutableChoiceKind::FullStruct, connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: None, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, }, )) }) diff --git a/crates/router/src/compatibility/stripe/setup_intents/types.rs b/crates/router/src/compatibility/stripe/setup_intents/types.rs index b5408f600b47..66c35637de71 100644 --- a/crates/router/src/compatibility/stripe/setup_intents/types.rs +++ b/crates/router/src/compatibility/stripe/setup_intents/types.rs @@ -189,13 +189,9 @@ impl TryFrom for payments::PaymentsRequest { .map(|connector| { api_models::routing::RoutingAlgorithm::Single(Box::new( api_models::routing::RoutableConnectorChoice { - #[cfg(feature = "backwards_compatibility")] choice_kind: api_models::routing::RoutableChoiceKind::FullStruct, connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: None, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, }, )) }) diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index af6d469fd3af..8a7854d658bf 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1340,13 +1340,9 @@ pub async fn create_payment_connector( if let Some(routable_connector_val) = routable_connector { let choice = routing_types::RoutableConnectorChoice { - #[cfg(feature = "backwards_compatibility")] choice_kind: routing_types::RoutableChoiceKind::FullStruct, connector: routable_connector_val, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: Some(mca.merchant_connector_id.clone()), - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: req.business_sub_label.clone(), }; if !default_routing_config.contains(&choice) { diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 24cc9583d907..5a4d40ef85ee 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -44,8 +44,6 @@ use super::surcharge_decision_configs::{ perform_surcharge_decision_management_for_payment_method_list, perform_surcharge_decision_management_for_saved_cards, }; -#[cfg(not(feature = "connector_choice_mca_id"))] -use crate::core::utils::get_connector_label; #[cfg(feature = "payouts")] use crate::types::domain::types::AsyncLift; use crate::{ @@ -2577,7 +2575,6 @@ pub async fn list_payment_methods( let mut routable_choice_list = vec![]; for choice in routing_choice { let routable_choice = routing_types::RoutableConnectorChoice { - #[cfg(feature = "backwards_compatibility")] choice_kind: routing_types::RoutableChoiceKind::FullStruct, connector: choice .connector @@ -2585,10 +2582,7 @@ pub async fn list_payment_methods( .to_string() .parse::() .change_context(errors::ApiErrorResponse::InternalServerError)?, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: choice.connector.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: choice.sub_label, }; routable_choice_list.push(routable_choice); } @@ -2619,22 +2613,6 @@ pub async fn list_payment_methods( .first() .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?; - #[cfg(not(feature = "connector_choice_mca_id"))] - let connector_label = get_connector_label( - payment_intent.business_country, - payment_intent.business_label.as_ref(), - #[cfg(not(feature = "connector_choice_mca_id"))] - first_routable_connector.sub_label.as_ref(), - #[cfg(feature = "connector_choice_mca_id")] - None, - first_routable_connector.connector.to_string().as_str(), - ); - #[cfg(not(feature = "connector_choice_mca_id"))] - let matched_mca = filtered_mcas - .iter() - .find(|m| connector_label == m.connector_label); - - #[cfg(feature = "connector_choice_mca_id")] let matched_mca = filtered_mcas.iter().find(|m| { first_routable_connector.merchant_connector_id.as_ref() == Some(&m.merchant_connector_id) @@ -3437,11 +3415,7 @@ pub async fn filter_payment_methods( connector_variant.to_string().as_str(), ) { context_values.push(dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, + api_models::routing::ast::ConnectorChoice { connector }, ))); }; diff --git a/crates/router/src/core/payment_methods/utils.rs b/crates/router/src/core/payment_methods/utils.rs index 96ed882f248a..7d90a5cb6bc0 100644 --- a/crates/router/src/core/payment_methods/utils.rs +++ b/crates/router/src/core/payment_methods/utils.rs @@ -311,11 +311,7 @@ fn construct_supported_connectors_for_update_mandate_node( .ok() .map(|connector| { dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, + api_models::routing::ast::ConnectorChoice { connector }, )) }) }), @@ -336,11 +332,7 @@ fn construct_supported_connectors_for_update_mandate_node( .ok() .map(|connector| { dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, + api_models::routing::ast::ConnectorChoice { connector }, )) }) }), @@ -393,11 +385,7 @@ fn construct_supported_connectors_for_update_mandate_node( .ok() .map(|connector| { dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, + api_models::routing::ast::ConnectorChoice { connector }, )) }) }), @@ -468,11 +456,7 @@ fn construct_supported_connectors_for_mandate_node( .filter_map(|connector| { match api_enums::RoutableConnectors::from_str(connector.to_string().as_str()) { Ok(connector) => Some(dir::DirValue::Connector(Box::new( - api_models::routing::ast::ConnectorChoice { - connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: None, - }, + api_models::routing::ast::ConnectorChoice { connector }, ))), Err(_) => None, } diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index c6a836f9c47e..5cad096bd395 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -3298,10 +3298,9 @@ where let mut routing_data = storage::RoutingData { routed_through: payment_data.payment_attempt.connector.clone(), - #[cfg(feature = "connector_choice_mca_id")] + merchant_connector_id: payment_data.payment_attempt.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - business_sub_label: payment_data.payment_attempt.business_sub_label.clone(), + algorithm: request_straight_through.clone(), routing_info: payment_data .payment_attempt @@ -3337,14 +3336,8 @@ where .attach_printable("error serializing payment routing info to serde value")?; payment_data.payment_attempt.connector = routing_data.routed_through; - #[cfg(feature = "connector_choice_mca_id")] - { - payment_data.payment_attempt.merchant_connector_id = routing_data.merchant_connector_id; - } - #[cfg(not(feature = "connector_choice_mca_id"))] - { - payment_data.payment_attempt.business_sub_label = routing_data.business_sub_label; - } + + payment_data.payment_attempt.merchant_connector_id = routing_data.merchant_connector_id; payment_data.payment_attempt.straight_through_algorithm = Some(encoded_info); Ok(decided_connector) @@ -3388,21 +3381,17 @@ where &state.conf.connectors, &mandate_connector_details.connector, api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] mandate_connector_details.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Invalid connector name received in 'routed_through'")?; routing_data.routed_through = Some(mandate_connector_details.connector.clone()); - #[cfg(feature = "connector_choice_mca_id")] - { - routing_data - .merchant_connector_id - .clone_from(&mandate_connector_details.merchant_connector_id); - } + + routing_data + .merchant_connector_id + .clone_from(&mandate_connector_details.merchant_connector_id); + return Ok(ConnectorCallType::PreDetermined(connector_data)); } @@ -3432,26 +3421,17 @@ where .ok_or(errors::ApiErrorResponse::IncorrectPaymentMethodConfiguration)?; routing_data.routed_through = Some(first_routable_connector.connector.to_string()); - #[cfg(feature = "connector_choice_mca_id")] - { - routing_data - .merchant_connector_id - .clone_from(&first_routable_connector.merchant_connector_id); - } - #[cfg(not(feature = "connector_choice_mca_id"))] - { - routing_data.business_sub_label = first_routable_connector.sub_label.clone(); - } + + routing_data + .merchant_connector_id + .clone_from(&first_routable_connector.merchant_connector_id); for connector_choice in routable_connector_list.clone() { let connector_data = api::ConnectorData::get_connector_by_name( &state.conf.connectors, &connector_choice.connector.to_string(), api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] connector_choice.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Invalid connector name received")?; @@ -3459,11 +3439,11 @@ where pre_routing_connector_data_list.push(connector_data); } - #[cfg(all(feature = "retry", feature = "connector_choice_mca_id"))] + #[cfg(feature = "retry")] let should_do_retry = retry::config_should_call_gsm(&*state.store, &merchant_account.merchant_id).await; - #[cfg(all(feature = "retry", feature = "connector_choice_mca_id"))] + #[cfg(feature = "retry")] if payment_data.payment_attempt.payment_method_type == Some(storage_enums::PaymentMethodType::ApplePay) && should_do_retry @@ -3510,19 +3490,14 @@ where .attach_printable("Failed execution of straight through routing")?; if check_eligibility { - #[cfg(feature = "business_profile_routing")] let profile_id = payment_data.payment_intent.profile_id.clone(); - #[cfg(not(feature = "business_profile_routing"))] - let _profile_id: Option = None; - connectors = routing::perform_eligibility_analysis_with_fallback( &state.clone(), key_store, connectors, &TransactionData::Payment(payment_data), eligible_connectors, - #[cfg(feature = "business_profile_routing")] profile_id, ) .await @@ -3537,10 +3512,7 @@ where &state.conf.connectors, &conn.connector.to_string(), api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] conn.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) }) .collect::, _>>() @@ -3567,19 +3539,14 @@ where .attach_printable("Failed execution of straight through routing")?; if check_eligibility { - #[cfg(feature = "business_profile_routing")] let profile_id = payment_data.payment_intent.profile_id.clone(); - #[cfg(not(feature = "business_profile_routing"))] - let _profile_id: Option = None; - connectors = routing::perform_eligibility_analysis_with_fallback( &state, key_store, connectors, &TransactionData::Payment(payment_data), eligible_connectors, - #[cfg(feature = "business_profile_routing")] profile_id, ) .await @@ -3594,10 +3561,7 @@ where &state.conf.connectors, &conn.connector.to_string(), api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] conn.merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) }) .collect::, _>>() @@ -3769,19 +3733,10 @@ pub async fn decide_multiplex_connector_for_normal_or_recurring_payment = Vec::new(); - #[cfg(not(feature = "connector_choice_mca_id"))] - for mut connector_data in connectors { - if !routing_enabled_pms.contains(&connector_data.payment_method_type) { - final_list.push(connector_data); - } else if let Some(choice) = result.get(&connector_data.payment_method_type) { - let routing_choice = choice - .first() - .ok_or(errors::ApiErrorResponse::InternalServerError)?; - if connector_data.connector.connector_name == routing_choice.connector.connector_name { - connector_data.business_sub_label = routing_choice.sub_label.clone(); - final_list.push(connector_data); - } - } - } - - #[cfg(feature = "connector_choice_mca_id")] for connector_data in connectors { if !routing_enabled_pms.contains(&connector_data.payment_method_type) { final_list.push(connector_data); @@ -3987,27 +3924,15 @@ where { #[allow(unused_variables)] let (profile_id, routing_algorithm) = match &transaction_data { - TransactionData::Payment(payment_data) => { - if cfg!(feature = "business_profile_routing") { - ( - payment_data.payment_intent.profile_id.clone(), - business_profile.routing_algorithm.clone(), - ) - } else { - (None, merchant_account.routing_algorithm.clone()) - } - } + TransactionData::Payment(payment_data) => ( + payment_data.payment_intent.profile_id.clone(), + business_profile.routing_algorithm.clone(), + ), #[cfg(feature = "payouts")] - TransactionData::Payout(payout_data) => { - if cfg!(feature = "business_profile_routing") { - ( - Some(payout_data.payout_attempt.profile_id.clone()), - business_profile.payout_routing_algorithm.clone(), - ) - } else { - (None, merchant_account.payout_routing_algorithm.clone()) - } - } + TransactionData::Payout(payout_data) => ( + Some(payout_data.payout_attempt.profile_id.clone()), + business_profile.payout_routing_algorithm.clone(), + ), }; let algorithm_ref = routing_algorithm @@ -4032,7 +3957,6 @@ where connectors, &transaction_data, eligible_connectors, - #[cfg(feature = "business_profile_routing")] profile_id, ) .await @@ -4053,10 +3977,7 @@ where &state.conf.connectors, &conn.connector.to_string(), api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] conn.merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) }) .collect::, _>>() @@ -4080,14 +4001,7 @@ where TransactionData::Payout(_) => { routing_data.routed_through = Some(first_connector_choice.connector.to_string()); - #[cfg(feature = "connector_choice_mca_id")] - { - routing_data.merchant_connector_id = first_connector_choice.merchant_connector_id; - } - #[cfg(not(feature = "connector_choice_mca_id"))] - { - routing_data.business_sub_label = first_connector_choice.sub_label; - } + routing_data.merchant_connector_id = first_connector_choice.merchant_connector_id; Ok(ConnectorCallType::Retryable(connector_data)) } diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 1579322995b5..3629457d5ab6 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -4220,7 +4220,7 @@ pub fn get_applepay_metadata( }) } -#[cfg(all(feature = "retry", feature = "connector_choice_mca_id"))] +#[cfg(feature = "retry")] pub async fn get_apple_pay_retryable_connectors( state: &SessionState, merchant_account: &domain::MerchantAccount, diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index d6904e09a3d5..0ff53c63b484 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -36,8 +36,6 @@ use storage_impl::redis::cache::{CacheKey, CGRAPH_CACHE, ROUTING_CACHE}; #[cfg(feature = "payouts")] use crate::core::payouts; -#[cfg(not(feature = "business_profile_routing"))] -use crate::utils::StringExt; use crate::{ core::{ errors, errors as oss_errors, payments as payments_oss, @@ -77,10 +75,6 @@ pub struct SessionRoutingPmTypeInput<'a> { routing_algorithm: &'a MerchantAccountRoutingAlgorithm, backend_input: dsl_inputs::BackendInput, allowed_connectors: FxHashMap, - #[cfg(any( - feature = "business_profile_routing", - feature = "profile_specific_fallback_routing" - ))] profile_id: Option, } @@ -262,10 +256,6 @@ pub async fn perform_static_routing_v1( algorithm_ref: routing_types::RoutingAlgorithmRef, transaction_data: &routing::TransactionData<'_, F>, ) -> RoutingResult> { - #[cfg(any( - feature = "profile_specific_fallback_routing", - feature = "business_profile_routing" - ))] let profile_id = match transaction_data { routing::TransactionData::Payment(payment_data) => payment_data .payment_intent @@ -281,9 +271,6 @@ pub async fn perform_static_routing_v1( } else { let fallback_config = routing_helpers::get_merchant_default_config( &*state.clone().store, - #[cfg(not(feature = "profile_specific_fallback_routing"))] - merchant_id, - #[cfg(feature = "profile_specific_fallback_routing")] profile_id, &api_enums::TransactionType::from(transaction_data), ) @@ -296,7 +283,6 @@ pub async fn perform_static_routing_v1( state, merchant_id, &algorithm_id, - #[cfg(feature = "business_profile_routing")] Some(profile_id).cloned(), &api_enums::TransactionType::from(transaction_data), ) @@ -328,10 +314,9 @@ async fn ensure_algorithm_cached_v1( state: &SessionState, merchant_id: &str, algorithm_id: &str, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, transaction_type: &api_enums::TransactionType, ) -> RoutingResult> { - #[cfg(feature = "business_profile_routing")] let key = { let profile_id = profile_id .clone() @@ -349,17 +334,6 @@ async fn ensure_algorithm_cached_v1( } }; - #[cfg(not(feature = "business_profile_routing"))] - let key = match transaction_type { - api_enums::TransactionType::Payment => { - format!("dsl_{merchant_id}") - } - #[cfg(feature = "payouts")] - api_enums::TransactionType::Payout => { - format!("dsl_po_{merchant_id}") - } - }; - let cached_algorithm = ROUTING_CACHE .get_val::>(CacheKey { key: key.clone(), @@ -370,14 +344,7 @@ async fn ensure_algorithm_cached_v1( let algorithm = if let Some(algo) = cached_algorithm { algo } else { - refresh_routing_cache_v1( - state, - key.clone(), - algorithm_id, - #[cfg(feature = "business_profile_routing")] - profile_id, - ) - .await? + refresh_routing_cache_v1(state, key.clone(), algorithm_id, profile_id).await? }; Ok(algorithm) @@ -429,9 +396,8 @@ pub async fn refresh_routing_cache_v1( state: &SessionState, key: String, algorithm_id: &str, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, ) -> RoutingResult> { - #[cfg(feature = "business_profile_routing")] let algorithm = { let algorithm = state .store @@ -448,22 +414,6 @@ pub async fn refresh_routing_cache_v1( algorithm }; - #[cfg(not(feature = "business_profile_routing"))] - let algorithm = { - let config = state - .store - .find_config_by_key(algorithm_id) - .await - .change_context(errors::RoutingError::DslMissingInDb) - .attach_printable("DSL not found in DB")?; - - let algorithm: routing_types::RoutingAlgorithm = config - .config - .parse_struct("Program") - .change_context(errors::RoutingError::DslParsingError) - .attach_printable("Error parsing routing algorithm from configs")?; - algorithm - }; let cached_algorithm = match algorithm { routing_types::RoutingAlgorithm::Single(conn) => CachedAlgorithm::Single(conn), routing_types::RoutingAlgorithm::Priority(plist) => CachedAlgorithm::Priority(plist), @@ -531,12 +481,11 @@ pub fn perform_volume_split( pub async fn get_merchant_cgraph<'a>( state: &SessionState, key_store: &domain::MerchantKeyStore, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, transaction_type: &api_enums::TransactionType, ) -> RoutingResult>> { let merchant_id = &key_store.merchant_id; - #[cfg(feature = "business_profile_routing")] let key = { let profile_id = profile_id .clone() @@ -551,13 +500,6 @@ pub async fn get_merchant_cgraph<'a>( } }; - #[cfg(not(feature = "business_profile_routing"))] - let key = match transaction_type { - api_enums::TransactionType::Payment => format!("cgraph_{}", merchant_id), - #[cfg(feature = "payouts")] - api_enums::TransactionType::Payout => format!("cgraph_po_{}", merchant_id), - }; - let cached_cgraph = CGRAPH_CACHE .get_val::>>( CacheKey { @@ -570,15 +512,7 @@ pub async fn get_merchant_cgraph<'a>( let cgraph = if let Some(graph) = cached_cgraph { graph } else { - refresh_cgraph_cache( - state, - key_store, - key.clone(), - #[cfg(feature = "business_profile_routing")] - profile_id, - transaction_type, - ) - .await? + refresh_cgraph_cache(state, key_store, key.clone(), profile_id, transaction_type).await? }; Ok(cgraph) @@ -588,7 +522,7 @@ pub async fn refresh_cgraph_cache<'a>( state: &SessionState, key_store: &domain::MerchantKeyStore, key: String, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, transaction_type: &api_enums::TransactionType, ) -> RoutingResult>> { let mut merchant_connector_accounts = state @@ -617,7 +551,6 @@ pub async fn refresh_cgraph_cache<'a>( } }; - #[cfg(feature = "business_profile_routing")] let merchant_connector_accounts = payments_oss::helpers::filter_mca_based_on_business_profile( merchant_connector_accounts, profile_id, @@ -679,7 +612,7 @@ async fn perform_cgraph_filtering( chosen: Vec, backend_input: dsl_inputs::BackendInput, eligible_connectors: Option<&Vec>, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, transaction_type: &api_enums::TransactionType, ) -> RoutingResult> { let context = euclid_graph::AnalysisContext::from_dir_values( @@ -687,14 +620,7 @@ async fn perform_cgraph_filtering( .into_context() .change_context(errors::RoutingError::KgraphAnalysisError)?, ); - let cached_cgraph = get_merchant_cgraph( - state, - key_store, - #[cfg(feature = "business_profile_routing")] - profile_id, - transaction_type, - ) - .await?; + let cached_cgraph = get_merchant_cgraph(state, key_store, profile_id, transaction_type).await?; let mut final_selection = Vec::::new(); for choice in chosen { @@ -730,7 +656,7 @@ pub async fn perform_eligibility_analysis( chosen: Vec, transaction_data: &routing::TransactionData<'_, F>, eligible_connectors: Option<&Vec>, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, ) -> RoutingResult> { let backend_input = match transaction_data { routing::TransactionData::Payment(payment_data) => make_dsl_input(payment_data)?, @@ -744,7 +670,6 @@ pub async fn perform_eligibility_analysis( chosen, backend_input, eligible_connectors, - #[cfg(feature = "business_profile_routing")] profile_id, &api_enums::TransactionType::from(transaction_data), ) @@ -756,13 +681,10 @@ pub async fn perform_fallback_routing( key_store: &domain::MerchantKeyStore, transaction_data: &routing::TransactionData<'_, F>, eligible_connectors: Option<&Vec>, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, ) -> RoutingResult> { let fallback_config = routing_helpers::get_merchant_default_config( &*state.store, - #[cfg(not(feature = "profile_specific_fallback_routing"))] - &key_store.merchant_id, - #[cfg(feature = "profile_specific_fallback_routing")] match transaction_data { routing::TransactionData::Payment(payment_data) => payment_data .payment_intent @@ -790,7 +712,6 @@ pub async fn perform_fallback_routing( fallback_config, backend_input, eligible_connectors, - #[cfg(feature = "business_profile_routing")] profile_id, &api_enums::TransactionType::from(transaction_data), ) @@ -803,7 +724,7 @@ pub async fn perform_eligibility_analysis_with_fallback( chosen: Vec, transaction_data: &routing::TransactionData<'_, F>, eligible_connectors: Option>, - #[cfg(feature = "business_profile_routing")] profile_id: Option, + profile_id: Option, ) -> RoutingResult> { let mut final_selection = perform_eligibility_analysis( state, @@ -811,7 +732,6 @@ pub async fn perform_eligibility_analysis_with_fallback( chosen, transaction_data, eligible_connectors.as_ref(), - #[cfg(feature = "business_profile_routing")] profile_id.clone(), ) .await?; @@ -821,7 +741,6 @@ pub async fn perform_eligibility_analysis_with_fallback( key_store, transaction_data, eligible_connectors.as_ref(), - #[cfg(feature = "business_profile_routing")] profile_id, ) .await; @@ -854,7 +773,6 @@ pub async fn perform_session_flow_routing( let mut pm_type_map: FxHashMap> = FxHashMap::default(); - #[cfg(feature = "business_profile_routing")] let routing_algorithm: MerchantAccountRoutingAlgorithm = { let profile_id = session_input .payment_intent @@ -879,18 +797,6 @@ pub async fn perform_session_flow_routing( .unwrap_or_default() }; - #[cfg(not(feature = "business_profile_routing"))] - let routing_algorithm: MerchantAccountRoutingAlgorithm = { - session_input - .merchant_account - .routing_algorithm - .clone() - .map(|val| val.parse_value("MerchantAccountRoutingAlgorithm")) - .transpose() - .change_context(errors::RoutingError::InvalidRoutingAlgorithmStructure)? - .unwrap_or_default() - }; - let payment_method_input = dsl_inputs::PaymentMethodInput { payment_method: None, payment_method_type: None, @@ -973,10 +879,7 @@ pub async fn perform_session_flow_routing( routing_algorithm: &routing_algorithm, backend_input: backend_input.clone(), allowed_connectors, - #[cfg(any( - feature = "business_profile_routing", - feature = "profile_specific_fallback_routing" - ))] + profile_id: session_input.payment_intent.profile_id.clone(), }; let routable_connector_choice_option = @@ -992,19 +895,12 @@ pub async fn perform_session_flow_routing( &session_pm_input.state.clone().conf.connectors, &connector_name, get_token.clone(), - #[cfg(feature = "connector_choice_mca_id")] selection.merchant_connector_id, - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) .change_context(errors::RoutingError::InvalidConnectorName(connector_name))?; - #[cfg(not(feature = "connector_choice_mca_id"))] - let sub_label = selection.sub_label; session_routing_choice.push(routing_types::SessionRoutingChoice { connector: connector_data, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: sub_label, payment_method_type: pm_type, }); } @@ -1031,7 +927,6 @@ async fn perform_session_routing_for_pm_type( &session_pm_input.state.clone(), merchant_id, algorithm_id, - #[cfg(feature = "business_profile_routing")] session_pm_input.profile_id.clone(), transaction_type, ) @@ -1052,16 +947,11 @@ async fn perform_session_routing_for_pm_type( } else { routing_helpers::get_merchant_default_config( &*session_pm_input.state.clone().store, - #[cfg(not(feature = "profile_specific_fallback_routing"))] - merchant_id, - #[cfg(feature = "profile_specific_fallback_routing")] - { - session_pm_input - .profile_id - .as_ref() - .get_required_value("profile_id") - .change_context(errors::RoutingError::ProfileIdMissing)? - }, + session_pm_input + .profile_id + .as_ref() + .get_required_value("profile_id") + .change_context(errors::RoutingError::ProfileIdMissing)?, transaction_type, ) .await @@ -1076,7 +966,6 @@ async fn perform_session_routing_for_pm_type( chosen_connectors, session_pm_input.backend_input.clone(), None, - #[cfg(feature = "business_profile_routing")] session_pm_input.profile_id.clone(), transaction_type, ) @@ -1085,16 +974,11 @@ async fn perform_session_routing_for_pm_type( if final_selection.is_empty() { let fallback = routing_helpers::get_merchant_default_config( &*session_pm_input.state.clone().store, - #[cfg(not(feature = "profile_specific_fallback_routing"))] - merchant_id, - #[cfg(feature = "profile_specific_fallback_routing")] - { - session_pm_input - .profile_id - .as_ref() - .get_required_value("profile_id") - .change_context(errors::RoutingError::ProfileIdMissing)? - }, + session_pm_input + .profile_id + .as_ref() + .get_required_value("profile_id") + .change_context(errors::RoutingError::ProfileIdMissing)?, transaction_type, ) .await @@ -1106,7 +990,6 @@ async fn perform_session_routing_for_pm_type( fallback, session_pm_input.backend_input.clone(), None, - #[cfg(feature = "business_profile_routing")] session_pm_input.profile_id.clone(), transaction_type, ) diff --git a/crates/router/src/core/payments/routing/transformers.rs b/crates/router/src/core/payments/routing/transformers.rs index ae779a6551f0..abe3fbd01b22 100644 --- a/crates/router/src/core/payments/routing/transformers.rs +++ b/crates/router/src/core/payments/routing/transformers.rs @@ -13,11 +13,7 @@ use crate::{ impl ForeignFrom for dsl_ast::ConnectorChoice { fn foreign_from(from: routing_types::RoutableConnectorChoice) -> Self { Self { - // #[cfg(feature = "backwards_compatibility")] - // choice_kind: from.choice_kind.foreign_into(), connector: from.connector, - #[cfg(not(feature = "connector_choice_mca_id"))] - sub_label: from.sub_label, } } } diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index e36036eb798c..4365519aecc4 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -112,10 +112,7 @@ pub async fn get_connector_choice( payout_data.payout_attempt.routing_info = Some(straight_through); let mut routing_data = storage::RoutingData { routed_through: connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: None, - #[cfg(not(feature = "connector_choice_mca_id"))] - business_sub_label: payout_data.payout_attempt.business_label.clone(), algorithm: Some(request_straight_through.clone()), routing_info: PaymentRoutingInfo { algorithm: None, @@ -137,10 +134,7 @@ pub async fn get_connector_choice( api::ConnectorChoice::Decide => { let mut routing_data = storage::RoutingData { routed_through: connector, - #[cfg(feature = "connector_choice_mca_id")] merchant_connector_id: None, - #[cfg(not(feature = "connector_choice_mca_id"))] - business_sub_label: payout_data.payout_attempt.business_label.clone(), algorithm: None, routing_info: PaymentRoutingInfo { algorithm: None, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 5d70e0092175..3ab171432022 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -690,7 +690,6 @@ pub async fn decide_payout_connector( connectors, &TransactionData::<()>::Payout(payout_data), eligible_connectors, - #[cfg(feature = "business_profile_routing")] Some(payout_attempt.profile_id.clone()), ) .await @@ -711,10 +710,7 @@ pub async fn decide_payout_connector( &state.conf.connectors, &conn.connector.to_string(), api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] payout_attempt.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) }) .collect::, _>>() @@ -722,14 +718,8 @@ pub async fn decide_payout_connector( .attach_printable("Invalid connector name received")?; routing_data.routed_through = Some(first_connector_choice.connector.to_string()); - #[cfg(feature = "connector_choice_mca_id")] - { - routing_data.merchant_connector_id = first_connector_choice.merchant_connector_id; - } - #[cfg(not(feature = "connector_choice_mca_id"))] - { - routing_data.business_sub_label = first_connector_choice.sub_label.clone(); - } + routing_data.merchant_connector_id = first_connector_choice.merchant_connector_id; + routing_data.routing_info.algorithm = Some(routing_algorithm); return Ok(api::ConnectorCallType::Retryable(connector_data)); } @@ -748,7 +738,6 @@ pub async fn decide_payout_connector( connectors, &TransactionData::<()>::Payout(payout_data), eligible_connectors, - #[cfg(feature = "business_profile_routing")] Some(payout_attempt.profile_id.clone()), ) .await @@ -771,10 +760,7 @@ pub async fn decide_payout_connector( &state.conf.connectors, &conn.connector.to_string(), api::GetToken::Connector, - #[cfg(feature = "connector_choice_mca_id")] payout_attempt.merchant_connector_id.clone(), - #[cfg(not(feature = "connector_choice_mca_id"))] - None, ) }) .collect::, _>>() @@ -782,14 +768,8 @@ pub async fn decide_payout_connector( .attach_printable("Invalid connector name received")?; routing_data.routed_through = Some(first_connector_choice.connector.to_string()); - #[cfg(feature = "connector_choice_mca_id")] - { - routing_data.merchant_connector_id = first_connector_choice.merchant_connector_id; - } - #[cfg(not(feature = "connector_choice_mca_id"))] - { - routing_data.business_sub_label = first_connector_choice.sub_label.clone(); - } + routing_data.merchant_connector_id = first_connector_choice.merchant_connector_id; + return Ok(api::ConnectorCallType::Retryable(connector_data)); } diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index c2312fe22456..5f10de56e471 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -1,42 +1,33 @@ pub mod helpers; pub mod transformers; -#[cfg(feature = "business_profile_routing")] -use api_models::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}; use api_models::{ enums, - routing::{self as routing_types, RoutingAlgorithmId}, + routing::{ + self as routing_types, RoutingAlgorithmId, RoutingRetrieveLinkQuery, RoutingRetrieveQuery, + }, }; -#[cfg(not(feature = "business_profile_routing"))] -use common_utils::ext_traits::{Encode, StringExt}; -#[cfg(not(feature = "business_profile_routing"))] -use diesel_models::configs; -#[cfg(feature = "business_profile_routing")] use diesel_models::routing_algorithm::RoutingAlgorithm; use error_stack::ResultExt; use rustc_hash::FxHashSet; -#[cfg(not(feature = "business_profile_routing"))] -use storage_impl::redis::cache; use super::payments; #[cfg(feature = "payouts")] use super::payouts; -#[cfg(feature = "business_profile_routing")] -use crate::types::transformers::{ForeignInto, ForeignTryFrom}; use crate::{ consts, core::{ - errors::{RouterResponse, StorageErrorExt}, + errors::{self, RouterResponse, StorageErrorExt}, metrics, utils as core_utils, }, routes::SessionState, - types::domain, + services::api as service_api, + types::{ + domain, + transformers::{ForeignInto, ForeignTryFrom}, + }, utils::{self, OptionExt, ValueExt}, }; -#[cfg(not(feature = "business_profile_routing"))] -use crate::{core::errors, services::api as service_api, types::storage}; -#[cfg(feature = "business_profile_routing")] -use crate::{errors, services::api as service_api}; pub enum TransactionData<'a, F> where @@ -50,47 +41,29 @@ where pub async fn retrieve_merchant_routing_dictionary( state: SessionState, merchant_account: domain::MerchantAccount, - #[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveQuery, - #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, + query_params: RoutingRetrieveQuery, + transaction_type: &enums::TransactionType, ) -> RouterResponse { metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE.add(&metrics::CONTEXT, 1, &[]); - #[cfg(feature = "business_profile_routing")] - { - let routing_metadata = state - .store - .list_routing_algorithm_metadata_by_merchant_id_transaction_type( - &merchant_account.merchant_id, - transaction_type, - i64::from(query_params.limit.unwrap_or_default()), - i64::from(query_params.offset.unwrap_or_default()), - ) - .await - .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - let result = routing_metadata - .into_iter() - .map(ForeignInto::foreign_into) - .collect::>(); - - metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE.add( - &metrics::CONTEXT, - 1, - &[], - ); - Ok(service_api::ApplicationResponse::Json( - routing_types::RoutingKind::RoutingAlgorithm(result), - )) - } - #[cfg(not(feature = "business_profile_routing"))] + + let routing_metadata = state + .store + .list_routing_algorithm_metadata_by_merchant_id_transaction_type( + &merchant_account.merchant_id, + transaction_type, + i64::from(query_params.limit.unwrap_or_default()), + i64::from(query_params.offset.unwrap_or_default()), + ) + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; + let result = routing_metadata + .into_iter() + .map(ForeignInto::foreign_into) + .collect::>(); + metrics::ROUTING_MERCHANT_DICTIONARY_RETRIEVE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - #[cfg(not(feature = "business_profile_routing"))] Ok(service_api::ApplicationResponse::Json( - routing_types::RoutingKind::Config( - helpers::get_merchant_routing_dictionary( - state.store.as_ref(), - &merchant_account.merchant_id, - ) - .await?, - ), + routing_types::RoutingKind::RoutingAlgorithm(result), )) } @@ -131,251 +104,121 @@ pub async fn create_routing_config( &format!("routing_{}", &merchant_account.merchant_id), ); - #[cfg(feature = "business_profile_routing")] - { - let profile_id = request - .profile_id - .get_required_value("profile_id") - .change_context(errors::ApiErrorResponse::MissingRequiredField { - field_name: "profile_id", - }) - .attach_printable("Profile_id not provided")?; - - core_utils::validate_and_get_business_profile( - db, - Some(&profile_id), - &merchant_account.merchant_id, - ) - .await?; - - helpers::validate_connectors_in_routing_config( - db, - &key_store, - &merchant_account.merchant_id, - &profile_id, - &algorithm, - ) - .await?; - - let timestamp = common_utils::date_time::now(); - let algo = RoutingAlgorithm { - algorithm_id: algorithm_id.clone(), - profile_id, - merchant_id: merchant_account.merchant_id, - name: name.clone(), - description: Some(description.clone()), - kind: algorithm.get_kind().foreign_into(), - algorithm_data: serde_json::json!(algorithm), - created_at: timestamp, - modified_at: timestamp, - algorithm_for: transaction_type.to_owned(), - }; - let record = db - .insert_routing_algorithm(algo) - .await - .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - - let new_record = record.foreign_into(); - - metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json(new_record)) - } - - #[cfg(not(feature = "business_profile_routing"))] - { - let algorithm_str = algorithm - .encode_to_string_of_json() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Unable to serialize routing algorithm to string")?; - - let mut algorithm_ref: routing_types::RoutingAlgorithmRef = merchant_account - .routing_algorithm - .clone() - .map(|val| val.parse_value("RoutingAlgorithmRef")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to deserialize routing algorithm ref from merchant account")? - .unwrap_or_default(); - let mut merchant_dictionary = - helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?; - - utils::when( - merchant_dictionary.records.len() >= consts::MAX_ROUTING_CONFIGS_PER_MERCHANT, - || { - Err(errors::ApiErrorResponse::PreconditionFailed { - message: format!("Reached the maximum number of routing configs ({}), please delete some to create new ones", consts::MAX_ROUTING_CONFIGS_PER_MERCHANT), + let profile_id = request + .profile_id + .get_required_value("profile_id") + .change_context(errors::ApiErrorResponse::MissingRequiredField { + field_name: "profile_id", }) - }, - )?; - let timestamp = common_utils::date_time::now_unix_timestamp(); - let records_are_empty = merchant_dictionary.records.is_empty(); - - let new_record = routing_types::RoutingDictionaryRecord { - id: algorithm_id.clone(), - name: name.clone(), - kind: algorithm.get_kind(), - description: description.clone(), - created_at: timestamp, - modified_at: timestamp, - algorithm_for: Some(*transaction_type), - }; - merchant_dictionary.records.push(new_record.clone()); - - let new_algorithm_config = configs::ConfigNew { - key: algorithm_id.clone(), - config: algorithm_str, - }; - - db.insert_config(new_algorithm_config) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to save new routing algorithm config to DB")?; + .attach_printable("Profile_id not provided")?; - if records_are_empty { - merchant_dictionary.active_id = Some(algorithm_id.clone()); - algorithm_ref.update_algorithm_id(algorithm_id); - let key = - cache::CacheKind::Routing(format!("dsl_{}", &merchant_account.merchant_id).into()); + core_utils::validate_and_get_business_profile( + db, + Some(&profile_id), + &merchant_account.merchant_id, + ) + .await?; - helpers::update_merchant_active_algorithm_ref(db, &key_store, key, algorithm_ref) - .await?; - } + helpers::validate_connectors_in_routing_config( + db, + &key_store, + &merchant_account.merchant_id, + &profile_id, + &algorithm, + ) + .await?; - helpers::update_merchant_routing_dictionary( - db, - &merchant_account.merchant_id, - merchant_dictionary, - ) - .await?; + let timestamp = common_utils::date_time::now(); + let algo = RoutingAlgorithm { + algorithm_id: algorithm_id.clone(), + profile_id, + merchant_id: merchant_account.merchant_id, + name: name.clone(), + description: Some(description.clone()), + kind: algorithm.get_kind().foreign_into(), + algorithm_data: serde_json::json!(algorithm), + created_at: timestamp, + modified_at: timestamp, + algorithm_for: transaction_type.to_owned(), + }; + let record = db + .insert_routing_algorithm(algo) + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json(new_record)) - } + let new_record = record.foreign_into(); + + metrics::ROUTING_CREATE_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); + Ok(service_api::ApplicationResponse::Json(new_record)) } pub async fn link_routing_config( state: SessionState, merchant_account: domain::MerchantAccount, - #[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore, algorithm_id: String, transaction_type: &enums::TransactionType, ) -> RouterResponse { metrics::ROUTING_LINK_CONFIG.add(&metrics::CONTEXT, 1, &[]); let db = state.store.as_ref(); - #[cfg(feature = "business_profile_routing")] - { - let routing_algorithm = db - .find_routing_algorithm_by_algorithm_id_merchant_id( - &algorithm_id, - &merchant_account.merchant_id, - ) - .await - .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?; - let business_profile = core_utils::validate_and_get_business_profile( - db, - Some(&routing_algorithm.profile_id), + let routing_algorithm = db + .find_routing_algorithm_by_algorithm_id_merchant_id( + &algorithm_id, &merchant_account.merchant_id, ) - .await? - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { - id: routing_algorithm.profile_id.clone(), - })?; + .await + .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?; - let mut routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type { - enums::TransactionType::Payment => business_profile.routing_algorithm.clone(), - #[cfg(feature = "payouts")] - enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(), - } + let business_profile = core_utils::validate_and_get_business_profile( + db, + Some(&routing_algorithm.profile_id), + &merchant_account.merchant_id, + ) + .await? + .get_required_value("BusinessProfile") + .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { + id: routing_algorithm.profile_id.clone(), + })?; + + let mut routing_ref: routing_types::RoutingAlgorithmRef = business_profile + .routing_algorithm + .clone() .map(|val| val.parse_value("RoutingAlgorithmRef")) .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("unable to deserialize routing algorithm ref from merchant account")? .unwrap_or_default(); - utils::when(routing_algorithm.algorithm_for != *transaction_type, || { + utils::when(routing_algorithm.algorithm_for != *transaction_type, || { + Err(errors::ApiErrorResponse::PreconditionFailed { + message: format!( + "Cannot use {}'s routing algorithm for {} operation", + routing_algorithm.algorithm_for, transaction_type + ), + }) + })?; + + utils::when( + routing_ref.algorithm_id == Some(algorithm_id.clone()), + || { Err(errors::ApiErrorResponse::PreconditionFailed { - message: format!( - "Cannot use {}'s routing algorithm for {} operation", - routing_algorithm.algorithm_for, transaction_type - ), + message: "Algorithm is already active".to_string(), }) - })?; - - utils::when( - routing_ref.algorithm_id == Some(algorithm_id.clone()), - || { - Err(errors::ApiErrorResponse::PreconditionFailed { - message: "Algorithm is already active".to_string(), - }) - }, - )?; - - routing_ref.update_algorithm_id(algorithm_id); - helpers::update_business_profile_active_algorithm_ref( - db, - business_profile, - routing_ref, - transaction_type, - ) - .await?; - - metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json( - routing_algorithm.foreign_into(), - )) - } - - #[cfg(not(feature = "business_profile_routing"))] - { - let mut routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type { - enums::TransactionType::Payment => merchant_account.routing_algorithm.clone(), - #[cfg(feature = "payouts")] - enums::TransactionType::Payout => merchant_account.payout_routing_algorithm.clone(), - } - .map(|val| val.parse_value("RoutingAlgorithmRef")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to deserialize routing algorithm ref from merchant account")? - .unwrap_or_default(); - - utils::when( - routing_ref.algorithm_id == Some(algorithm_id.clone()), - || { - Err(errors::ApiErrorResponse::PreconditionFailed { - message: "Algorithm is already active".to_string(), - }) - }, - )?; - let mut merchant_dictionary = - helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?; - - let modified_at = common_utils::date_time::now_unix_timestamp(); - let record = merchant_dictionary - .records - .iter_mut() - .find(|rec| rec.id == algorithm_id) - .ok_or(errors::ApiErrorResponse::ResourceIdNotFound) - .attach_printable("Record with given ID not found for routing config activation")?; - - record.modified_at = modified_at; - merchant_dictionary.active_id = Some(record.id.clone()); - let response = record.clone(); - routing_ref.update_algorithm_id(algorithm_id); - helpers::update_merchant_routing_dictionary( - db, - &merchant_account.merchant_id, - merchant_dictionary, - ) - .await?; - let key = - cache::CacheKind::Routing(format!("dsl_{}", &merchant_account.merchant_id).into()); - helpers::update_merchant_active_algorithm_ref(db, &key_store, key, routing_ref).await?; + }, + )?; + routing_ref.update_algorithm_id(algorithm_id); + helpers::update_business_profile_active_algorithm_ref( + db, + business_profile, + routing_ref, + transaction_type, + ) + .await?; - metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json(response)) - } + metrics::ROUTING_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); + Ok(service_api::ApplicationResponse::Json( + routing_algorithm.foreign_into(), + )) } pub async fn retrieve_routing_config( @@ -385,256 +228,105 @@ pub async fn retrieve_routing_config( ) -> RouterResponse { metrics::ROUTING_RETRIEVE_CONFIG.add(&metrics::CONTEXT, 1, &[]); let db = state.store.as_ref(); - #[cfg(feature = "business_profile_routing")] - { - let routing_algorithm = db - .find_routing_algorithm_by_algorithm_id_merchant_id( - &algorithm_id.0, - &merchant_account.merchant_id, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - core_utils::validate_and_get_business_profile( - db, - Some(&routing_algorithm.profile_id), + let routing_algorithm = db + .find_routing_algorithm_by_algorithm_id_merchant_id( + &algorithm_id.0, &merchant_account.merchant_id, ) - .await? - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?; - - let response = routing_types::MerchantRoutingAlgorithm::foreign_try_from(routing_algorithm) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to parse routing algorithm")?; - - metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json(response)) - } - - #[cfg(not(feature = "business_profile_routing"))] - { - let merchant_dictionary = - helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?; + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - let record = merchant_dictionary - .records - .into_iter() - .find(|rec| rec.id == algorithm_id.0) - .ok_or(errors::ApiErrorResponse::ResourceIdNotFound) - .attach_printable("Algorithm with the given ID not found in the merchant dictionary")?; + core_utils::validate_and_get_business_profile( + db, + Some(&routing_algorithm.profile_id), + &merchant_account.merchant_id, + ) + .await? + .get_required_value("BusinessProfile") + .change_context(errors::ApiErrorResponse::ResourceIdNotFound)?; - let algorithm_config = db - .find_config_by_key(&algorithm_id.0) - .await - .change_context(errors::ApiErrorResponse::ResourceIdNotFound) - .attach_printable("Routing config not found in DB")?; + let response = routing_types::MerchantRoutingAlgorithm::foreign_try_from(routing_algorithm) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to parse routing algorithm")?; - let algorithm: routing_types::RoutingAlgorithm = algorithm_config - .config - .parse_struct("RoutingAlgorithm") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Error deserializing routing algorithm config")?; - - let response = routing_types::MerchantRoutingAlgorithm { - id: record.id, - name: record.name, - description: record.description, - algorithm, - created_at: record.created_at, - modified_at: record.modified_at, - algorithm_for: record - .algorithm_for - .unwrap_or(enums::TransactionType::Payment), - }; - - metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json(response)) - } + metrics::ROUTING_RETRIEVE_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); + Ok(service_api::ApplicationResponse::Json(response)) } pub async fn unlink_routing_config( state: SessionState, merchant_account: domain::MerchantAccount, - #[cfg(not(feature = "business_profile_routing"))] key_store: domain::MerchantKeyStore, - #[cfg(feature = "business_profile_routing")] request: routing_types::RoutingConfigRequest, + request: routing_types::RoutingConfigRequest, transaction_type: &enums::TransactionType, ) -> RouterResponse { metrics::ROUTING_UNLINK_CONFIG.add(&metrics::CONTEXT, 1, &[]); let db = state.store.as_ref(); - #[cfg(feature = "business_profile_routing")] - { - let profile_id = request - .profile_id - .get_required_value("profile_id") - .change_context(errors::ApiErrorResponse::MissingRequiredField { - field_name: "profile_id", - }) - .attach_printable("Profile_id not provided")?; - let business_profile = core_utils::validate_and_get_business_profile( - db, - Some(&profile_id), - &merchant_account.merchant_id, - ) - .await?; - match business_profile { - Some(business_profile) => { - let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type { - enums::TransactionType::Payment => business_profile.routing_algorithm.clone(), - #[cfg(feature = "payouts")] - enums::TransactionType::Payout => { - business_profile.payout_routing_algorithm.clone() - } - } - .map(|val| val.parse_value("RoutingAlgorithmRef")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable( - "unable to deserialize routing algorithm ref from merchant account", - )? - .unwrap_or_default(); - - let timestamp = common_utils::date_time::now_unix_timestamp(); - - match routing_algo_ref.algorithm_id { - Some(algorithm_id) => { - let routing_algorithm: routing_types::RoutingAlgorithmRef = - routing_types::RoutingAlgorithmRef { - algorithm_id: None, - timestamp, - config_algo_id: routing_algo_ref.config_algo_id.clone(), - surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id, - }; - - let record = db - .find_routing_algorithm_by_profile_id_algorithm_id( - &profile_id, - &algorithm_id, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - let response = record.foreign_into(); - helpers::update_business_profile_active_algorithm_ref( - db, - business_profile, - routing_algorithm, - transaction_type, + let profile_id = request + .profile_id + .get_required_value("profile_id") + .change_context(errors::ApiErrorResponse::MissingRequiredField { + field_name: "profile_id", + }) + .attach_printable("Profile_id not provided")?; + let business_profile = core_utils::validate_and_get_business_profile( + db, + Some(&profile_id), + &merchant_account.merchant_id, + ) + .await?; + match business_profile { + Some(business_profile) => { + let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type { + enums::TransactionType::Payment => business_profile.routing_algorithm.clone(), + #[cfg(feature = "payouts")] + enums::TransactionType::Payout => business_profile.payout_routing_algorithm.clone(), + } + .map(|val| val.parse_value("RoutingAlgorithmRef")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to deserialize routing algorithm ref from merchant account")? + .unwrap_or_default(); + + let timestamp = common_utils::date_time::now_unix_timestamp(); + + match routing_algo_ref.algorithm_id { + Some(algorithm_id) => { + let routing_algorithm: routing_types::RoutingAlgorithmRef = + routing_types::RoutingAlgorithmRef { + algorithm_id: None, + timestamp, + config_algo_id: routing_algo_ref.config_algo_id.clone(), + surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id, + }; + + let record = db + .find_routing_algorithm_by_profile_id_algorithm_id( + &profile_id, + &algorithm_id, ) - .await?; - - metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add( - &metrics::CONTEXT, - 1, - &[], - ); - Ok(service_api::ApplicationResponse::Json(response)) - } - None => Err(errors::ApiErrorResponse::PreconditionFailed { - message: "Algorithm is already inactive".to_string(), - })?, + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; + let response = record.foreign_into(); + helpers::update_business_profile_active_algorithm_ref( + db, + business_profile, + routing_algorithm, + transaction_type, + ) + .await?; + + metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); + Ok(service_api::ApplicationResponse::Json(response)) } + None => Err(errors::ApiErrorResponse::PreconditionFailed { + message: "Algorithm is already inactive".to_string(), + })?, } - None => Err(errors::ApiErrorResponse::InvalidRequestData { - message: "The business_profile is not present".to_string(), - } - .into()), } - } - - #[cfg(not(feature = "business_profile_routing"))] - { - let mut merchant_dictionary = - helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?; - - let routing_algo_ref: routing_types::RoutingAlgorithmRef = match transaction_type { - enums::TransactionType::Payment => merchant_account.routing_algorithm.clone(), - #[cfg(feature = "payouts")] - enums::TransactionType::Payout => merchant_account.payout_routing_algorithm.clone(), + None => Err(errors::ApiErrorResponse::InvalidRequestData { + message: "The business_profile is not present".to_string(), } - .map(|val| val.parse_value("RoutingAlgorithmRef")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to deserialize routing algorithm ref from merchant account")? - .unwrap_or_default(); - let timestamp = common_utils::date_time::now_unix_timestamp(); - - utils::when(routing_algo_ref.algorithm_id.is_none(), || { - Err(errors::ApiErrorResponse::PreconditionFailed { - message: "Algorithm is already inactive".to_string(), - }) - })?; - let routing_algorithm: routing_types::RoutingAlgorithmRef = - routing_types::RoutingAlgorithmRef { - algorithm_id: None, - timestamp, - config_algo_id: routing_algo_ref.config_algo_id.clone(), - surcharge_config_algo_id: routing_algo_ref.surcharge_config_algo_id, - }; - - let active_algorithm_id = merchant_dictionary - .active_id - .or(routing_algo_ref.algorithm_id.clone()) - .ok_or(errors::ApiErrorResponse::PreconditionFailed { - // When the merchant_dictionary doesn't have any active algorithm and merchant_account doesn't have any routing_algorithm configured - message: "Algorithm is already inactive".to_string(), - })?; - - let record = merchant_dictionary - .records - .iter_mut() - .find(|rec| rec.id == active_algorithm_id) - .ok_or(errors::ApiErrorResponse::ResourceIdNotFound) - .attach_printable("Record with the given ID not found for de-activation")?; - - let response = record.clone(); - - merchant_dictionary.active_id = None; - - helpers::update_merchant_routing_dictionary( - db, - &merchant_account.merchant_id, - merchant_dictionary, - ) - .await?; - - let ref_value = routing_algorithm - .encode_to_value() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed converting routing algorithm ref to json value")?; - - let merchant_account_update = storage::MerchantAccountUpdate::Update { - merchant_name: None, - merchant_details: None, - return_url: None, - webhook_details: None, - sub_merchants_enabled: None, - parent_merchant_id: None, - enable_payment_response_hash: None, - payment_response_hash_key: None, - redirect_to_merchant_with_http_post: None, - publishable_key: None, - locker_id: None, - metadata: None, - routing_algorithm: Some(ref_value), - primary_business_details: None, - intent_fulfillment_time: None, - frm_routing_algorithm: None, - payout_routing_algorithm: None, - default_profile: None, - payment_link_config: None, - pm_collect_link_config: None, - }; - - db.update_specific_fields_in_merchant( - &key_store.merchant_id, - merchant_account_update, - &key_store, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to update routing algorithm ref in merchant account")?; - - metrics::ROUTING_UNLINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json(response)) + .into()), } } @@ -710,110 +402,60 @@ pub async fn retrieve_default_routing_config( pub async fn retrieve_linked_routing_config( state: SessionState, merchant_account: domain::MerchantAccount, - #[cfg(feature = "business_profile_routing")] query_params: RoutingRetrieveLinkQuery, - #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, + query_params: RoutingRetrieveLinkQuery, + transaction_type: &enums::TransactionType, ) -> RouterResponse { metrics::ROUTING_RETRIEVE_LINK_CONFIG.add(&metrics::CONTEXT, 1, &[]); let db = state.store.as_ref(); - #[cfg(feature = "business_profile_routing")] - { - let business_profiles = if let Some(profile_id) = query_params.profile_id { - core_utils::validate_and_get_business_profile( - db, - Some(&profile_id), - &merchant_account.merchant_id, - ) - .await? - .map(|profile| vec![profile]) - .get_required_value("BusinessProfile") - .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_id })? - } else { - db.list_business_profile_by_merchant_id(&merchant_account.merchant_id) - .await - .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)? - }; - - let mut active_algorithms = Vec::new(); - - for business_profile in business_profiles { - let routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type { - enums::TransactionType::Payment => business_profile.routing_algorithm, - #[cfg(feature = "payouts")] - enums::TransactionType::Payout => business_profile.payout_routing_algorithm, - } - .clone() - .map(|val| val.parse_value("RoutingAlgorithmRef")) - .transpose() - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to deserialize routing algorithm ref from merchant account")? - .unwrap_or_default(); + let business_profiles = if let Some(profile_id) = query_params.profile_id { + core_utils::validate_and_get_business_profile( + db, + Some(&profile_id), + &merchant_account.merchant_id, + ) + .await? + .map(|profile| vec![profile]) + .get_required_value("BusinessProfile") + .change_context(errors::ApiErrorResponse::BusinessProfileNotFound { id: profile_id })? + } else { + db.list_business_profile_by_merchant_id(&merchant_account.merchant_id) + .await + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)? + }; - if let Some(algorithm_id) = routing_ref.algorithm_id { - let record = db - .find_routing_algorithm_metadata_by_algorithm_id_profile_id( - &algorithm_id, - &business_profile.profile_id, - ) - .await - .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; + let mut active_algorithms = Vec::new(); - active_algorithms.push(record.foreign_into()); - } + for business_profile in business_profiles { + let routing_ref: routing_types::RoutingAlgorithmRef = match transaction_type { + enums::TransactionType::Payment => business_profile.routing_algorithm, + #[cfg(feature = "payouts")] + enums::TransactionType::Payout => business_profile.payout_routing_algorithm, } + .clone() + .map(|val| val.parse_value("RoutingAlgorithmRef")) + .transpose() + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to deserialize routing algorithm ref from merchant account")? + .unwrap_or_default(); - metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json( - routing_types::LinkedRoutingConfigRetrieveResponse::ProfileBased(active_algorithms), - )) - } - #[cfg(not(feature = "business_profile_routing"))] - { - let merchant_dictionary = - helpers::get_merchant_routing_dictionary(db, &merchant_account.merchant_id).await?; - - let algorithm = if let Some(algorithm_id) = merchant_dictionary.active_id { - let record = merchant_dictionary - .records - .into_iter() - .find(|rec| rec.id == algorithm_id) - .ok_or(errors::ApiErrorResponse::ResourceIdNotFound) - .attach_printable("record for active algorithm not found in merchant dictionary")?; - - let config = db - .find_config_by_key(&algorithm_id) + if let Some(algorithm_id) = routing_ref.algorithm_id { + let record = db + .find_routing_algorithm_metadata_by_algorithm_id_profile_id( + &algorithm_id, + &business_profile.profile_id, + ) .await - .to_not_found_response(errors::ApiErrorResponse::InternalServerError) - .attach_printable("error finding routing config in db")?; - - let the_algorithm: routing_types::RoutingAlgorithm = config - .config - .parse_struct("RoutingAlgorithm") - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to parse routing algorithm")?; - - Some(routing_types::MerchantRoutingAlgorithm { - id: record.id, - name: record.name, - description: record.description, - algorithm: the_algorithm, - created_at: record.created_at, - modified_at: record.modified_at, - algorithm_for: record - .algorithm_for - .unwrap_or(enums::TransactionType::Payment), - }) - } else { - None - }; - - let response = routing_types::LinkedRoutingConfigRetrieveResponse::MerchantAccountBased( - routing_types::RoutingRetrieveResponse { algorithm }, - ); + .to_not_found_response(errors::ApiErrorResponse::ResourceIdNotFound)?; - metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); - Ok(service_api::ApplicationResponse::Json(response)) + active_algorithms.push(record.foreign_into()); + } } + + metrics::ROUTING_RETRIEVE_LINK_CONFIG_SUCCESS_RESPONSE.add(&metrics::CONTEXT, 1, &[]); + Ok(service_api::ApplicationResponse::Json( + routing_types::LinkedRoutingConfigRetrieveResponse::ProfileBased(active_algorithms), + )) } pub async fn retrieve_default_routing_config_for_profiles( @@ -883,25 +525,17 @@ pub async fn update_default_routing_config_for_profile( }) })?; - let existing_set = FxHashSet::from_iter(default_config.iter().map(|c| { - ( - c.connector.to_string(), - #[cfg(feature = "connector_choice_mca_id")] - c.merchant_connector_id.as_ref(), - #[cfg(not(feature = "connector_choice_mca_id"))] - c.sub_label.as_ref(), - ) - })); - - let updated_set = FxHashSet::from_iter(updated_config.iter().map(|c| { - ( - c.connector.to_string(), - #[cfg(feature = "connector_choice_mca_id")] - c.merchant_connector_id.as_ref(), - #[cfg(not(feature = "connector_choice_mca_id"))] - c.sub_label.as_ref(), - ) - })); + let existing_set = FxHashSet::from_iter( + default_config + .iter() + .map(|c| (c.connector.to_string(), c.merchant_connector_id.as_ref())), + ); + + let updated_set = FxHashSet::from_iter( + updated_config + .iter() + .map(|c| (c.connector.to_string(), c.merchant_connector_id.as_ref())), + ); let symmetric_diff = existing_set .symmetric_difference(&updated_set) diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index 0e4adbff36b1..d99177aa5557 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -249,14 +249,11 @@ pub async fn update_business_profile_active_algorithm_ref( let merchant_id = current_business_profile.merchant_id.clone(); - #[cfg(feature = "business_profile_routing")] let profile_id = current_business_profile.profile_id.clone(); - #[cfg(feature = "business_profile_routing")] + let routing_cache_key = cache::CacheKind::Routing(format!("routing_config_{merchant_id}_{profile_id}").into()); - #[cfg(not(feature = "business_profile_routing"))] - let routing_cache_key = cache::CacheKind::Routing(format!("dsl_{merchant_id}").into()); let (routing_algorithm, payout_routing_algorithm) = match transaction_type { storage::enums::TransactionType::Payment => (Some(ref_val), None), #[cfg(feature = "payouts")] @@ -320,7 +317,6 @@ pub async fn validate_connectors_in_routing_config( id: merchant_id.to_string(), })?; - #[cfg(feature = "connector_choice_mca_id")] let name_mca_id_set = all_mcas .iter() .filter(|mca| mca.profile_id.as_deref() == Some(profile_id)) @@ -333,7 +329,6 @@ pub async fn validate_connectors_in_routing_config( .map(|mca| &mca.connector_name) .collect::>(); - #[cfg(feature = "connector_choice_mca_id")] let check_connector_choice = |choice: &routing_types::RoutableConnectorChoice| { if let Some(ref mca_id) = choice.merchant_connector_id { error_stack::ensure!( @@ -361,21 +356,6 @@ pub async fn validate_connectors_in_routing_config( Ok(()) }; - #[cfg(not(feature = "connector_choice_mca_id"))] - let check_connector_choice = |choice: &routing_types::RoutableConnectorChoice| { - error_stack::ensure!( - name_set.contains(&choice.connector.to_string()), - errors::ApiErrorResponse::InvalidRequestData { - message: format!( - "connector with name '{}' not found for the given profile", - choice.connector, - ) - } - ); - - Ok(()) - }; - match routing_algorithm { routing_types::RoutingAlgorithm::Single(choice) => { check_connector_choice(choice)?; diff --git a/crates/router/src/core/routing/transformers.rs b/crates/router/src/core/routing/transformers.rs index 4feca317a559..2e45605edb6a 100644 --- a/crates/router/src/core/routing/transformers.rs +++ b/crates/router/src/core/routing/transformers.rs @@ -17,7 +17,7 @@ impl ForeignFrom for RoutingDictionaryRecord { fn foreign_from(value: RoutingProfileMetadata) -> Self { Self { id: value.algorithm_id, - #[cfg(feature = "business_profile_routing")] + profile_id: value.profile_id, name: value.name, kind: value.kind.foreign_into(), @@ -33,7 +33,7 @@ impl ForeignFrom for RoutingDictionaryRecord { fn foreign_from(value: RoutingAlgorithm) -> Self { Self { id: value.algorithm_id, - #[cfg(feature = "business_profile_routing")] + profile_id: value.profile_id, name: value.name, kind: value.kind.foreign_into(), @@ -52,7 +52,7 @@ impl ForeignTryFrom for MerchantRoutingAlgorithm { Ok(Self { id: value.algorithm_id, name: value.name, - #[cfg(feature = "business_profile_routing")] + profile_id: value.profile_id, description: value.description.unwrap_or_default(), algorithm: value diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index 39d63080a4b0..ff3830b23810 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -513,7 +513,6 @@ async fn publish_and_redact_merchant_account_cache( .as_ref() .map(|publishable_key| CacheKind::Accounts(publishable_key.into())); - #[cfg(feature = "business_profile_routing")] let cgraph_key = merchant_account.default_profile.as_ref().map(|profile_id| { CacheKind::CGraph( format!( @@ -525,11 +524,6 @@ async fn publish_and_redact_merchant_account_cache( ) }); - #[cfg(not(feature = "business_profile_routing"))] - let cgraph_key = Some(CacheKind::CGraph( - format!("cgraph_{}", merchant_account.merchant_id.clone()).into(), - )); - let mut cache_keys = vec![CacheKind::Accounts( merchant_account.merchant_id.as_str().into(), )]; diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index b8b329a3acb8..0d7d042f7af3 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, sync::Arc}; use actix_web::{web, Scope}; -#[cfg(all(feature = "business_profile_routing", feature = "olap"))] +#[cfg(feature = "olap")] use api_models::routing::RoutingRetrieveQuery; #[cfg(feature = "olap")] use common_enums::TransactionType; @@ -569,22 +569,19 @@ impl Routing { #[allow(unused_mut)] let mut route = web::scope("/routing") .app_data(web::Data::new(state.clone())) - .service(web::resource("/active").route(web::get().to( - |state, req, #[cfg(feature = "business_profile_routing")] query_params| { + .service( + web::resource("/active").route(web::get().to(|state, req, query_params| { cloud_routing::routing_retrieve_linked_config( state, req, - #[cfg(feature = "business_profile_routing")] query_params, - #[cfg(feature = "business_profile_routing")] &TransactionType::Payment, ) - }, - ))) + })), + ) .service( web::resource("") .route( - #[cfg(feature = "business_profile_routing")] web::get().to(|state, req, path: web::Query| { cloud_routing::list_routing_configs( state, @@ -593,8 +590,6 @@ impl Routing { &TransactionType::Payment, ) }), - #[cfg(not(feature = "business_profile_routing"))] - web::get().to(cloud_routing::list_routing_configs), ) .route(web::post().to(|state, req, payload| { cloud_routing::routing_create_config( @@ -623,17 +618,16 @@ impl Routing { ) })), ) - .service(web::resource("/deactivate").route(web::post().to( - |state, req, #[cfg(feature = "business_profile_routing")] payload| { + .service( + web::resource("/deactivate").route(web::post().to(|state, req, payload| { cloud_routing::routing_unlink_config( state, req, - #[cfg(feature = "business_profile_routing")] payload, &TransactionType::Payment, ) - }, - ))) + })), + ) .service( web::resource("/decision") .route(web::put().to(cloud_routing::upsert_decision_manager_config)) @@ -676,21 +670,16 @@ impl Routing { route = route .service( web::resource("/payouts") - .route( - #[cfg(feature = "business_profile_routing")] - web::get().to(|state, req, path: web::Query| { + .route(web::get().to( + |state, req, path: web::Query| { cloud_routing::list_routing_configs( state, req, - #[cfg(feature = "business_profile_routing")] path, - #[cfg(feature = "business_profile_routing")] &TransactionType::Payout, ) - }), - #[cfg(not(feature = "business_profile_routing"))] - web::get().to(cloud_routing::list_routing_configs), - ) + }, + )) .route(web::post().to(|state, req, payload| { cloud_routing::routing_create_config( state, @@ -701,13 +690,11 @@ impl Routing { })), ) .service(web::resource("/payouts/active").route(web::get().to( - |state, req, #[cfg(feature = "business_profile_routing")] query_params| { + |state, req, query_params| { cloud_routing::routing_retrieve_linked_config( state, req, - #[cfg(feature = "business_profile_routing")] query_params, - #[cfg(feature = "business_profile_routing")] &TransactionType::Payout, ) }, @@ -743,11 +730,10 @@ impl Routing { )), ) .service(web::resource("/payouts/deactivate").route(web::post().to( - |state, req, #[cfg(feature = "business_profile_routing")] payload| { + |state, req, payload| { cloud_routing::routing_unlink_config( state, req, - #[cfg(feature = "business_profile_routing")] payload, &TransactionType::Payout, ) diff --git a/crates/router/src/routes/routing.rs b/crates/router/src/routes/routing.rs index 74bd61765b0d..788acd4ba2da 100644 --- a/crates/router/src/routes/routing.rs +++ b/crates/router/src/routes/routing.rs @@ -3,9 +3,10 @@ //! Functions that are used to perform the api level configuration, retrieval, updation //! of Routing configs. use actix_web::{web, HttpRequest, Responder}; -#[cfg(feature = "business_profile_routing")] -use api_models::routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}; -use api_models::{enums, routing as routing_types}; +use api_models::{ + enums, routing as routing_types, + routing::{RoutingRetrieveLinkQuery, RoutingRetrieveQuery}, +}; use router_env::{ tracing::{self, instrument}, Flow, @@ -16,7 +17,6 @@ use crate::{ routes::AppState, services::{api as oss_api, authentication as auth, authorization::permissions::Permission}, }; - #[cfg(feature = "olap")] #[instrument(skip_all)] pub async fn routing_create_config( @@ -71,8 +71,6 @@ pub async fn routing_link_config( routing::link_routing_config( state, auth.merchant_account, - #[cfg(not(feature = "business_profile_routing"))] - auth.key_store, algorithm_id.0, transaction_type, ) @@ -125,61 +123,34 @@ pub async fn routing_retrieve_config( pub async fn list_routing_configs( state: web::Data, req: HttpRequest, - #[cfg(feature = "business_profile_routing")] query: web::Query, - #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, + query: web::Query, + transaction_type: &enums::TransactionType, ) -> impl Responder { - #[cfg(feature = "business_profile_routing")] - { - let flow = Flow::RoutingRetrieveDictionary; - Box::pin(oss_api::server_wrap( - flow, - state, - &req, - query.into_inner(), - |state, auth: auth::AuthenticationData, query_params, _| { - routing::retrieve_merchant_routing_dictionary( - state, - auth.merchant_account, - query_params, - transaction_type, - ) - }, - #[cfg(not(feature = "release"))] - auth::auth_type( - &auth::ApiKeyAuth, - &auth::JWTAuth(Permission::RoutingRead), - req.headers(), - ), - #[cfg(feature = "release")] - &auth::JWTAuth(Permission::RoutingRead), - api_locking::LockAction::NotApplicable, - )) - .await - } - - #[cfg(not(feature = "business_profile_routing"))] - { - let flow = Flow::RoutingRetrieveDictionary; - Box::pin(oss_api::server_wrap( - flow, - state, - &req, - (), - |state, auth: auth::AuthenticationData, _, _| { - routing::retrieve_merchant_routing_dictionary(state, auth.merchant_account) - }, - #[cfg(not(feature = "release"))] - auth::auth_type( - &auth::ApiKeyAuth, - &auth::JWTAuth(Permission::RoutingRead), - req.headers(), - ), - #[cfg(feature = "release")] + let flow = Flow::RoutingRetrieveDictionary; + Box::pin(oss_api::server_wrap( + flow, + state, + &req, + query.into_inner(), + |state, auth: auth::AuthenticationData, query_params, _| { + routing::retrieve_merchant_routing_dictionary( + state, + auth.merchant_account, + query_params, + transaction_type, + ) + }, + #[cfg(not(feature = "release"))] + auth::auth_type( + &auth::ApiKeyAuth, &auth::JWTAuth(Permission::RoutingRead), - api_locking::LockAction::NotApplicable, - )) - .await - } + req.headers(), + ), + #[cfg(feature = "release")] + &auth::JWTAuth(Permission::RoutingRead), + api_locking::LockAction::NotApplicable, + )) + .await } #[cfg(feature = "olap")] @@ -187,68 +158,34 @@ pub async fn list_routing_configs( pub async fn routing_unlink_config( state: web::Data, req: HttpRequest, - #[cfg(feature = "business_profile_routing")] payload: web::Json< - routing_types::RoutingConfigRequest, - >, + payload: web::Json, transaction_type: &enums::TransactionType, ) -> impl Responder { - #[cfg(feature = "business_profile_routing")] - { - let flow = Flow::RoutingUnlinkConfig; - Box::pin(oss_api::server_wrap( - flow, - state, - &req, - payload.into_inner(), - |state, auth: auth::AuthenticationData, payload_req, _| { - routing::unlink_routing_config( - state, - auth.merchant_account, - payload_req, - transaction_type, - ) - }, - #[cfg(not(feature = "release"))] - auth::auth_type( - &auth::ApiKeyAuth, - &auth::JWTAuth(Permission::RoutingWrite), - req.headers(), - ), - #[cfg(feature = "release")] - &auth::JWTAuth(Permission::RoutingWrite), - api_locking::LockAction::NotApplicable, - )) - .await - } - - #[cfg(not(feature = "business_profile_routing"))] - { - let flow = Flow::RoutingUnlinkConfig; - Box::pin(oss_api::server_wrap( - flow, - state, - &req, - (), - |state, auth: auth::AuthenticationData, _, _| { - routing::unlink_routing_config( - state, - auth.merchant_account, - auth.key_store, - transaction_type, - ) - }, - #[cfg(not(feature = "release"))] - auth::auth_type( - &auth::ApiKeyAuth, - &auth::JWTAuth(Permission::RoutingWrite), - req.headers(), - ), - #[cfg(feature = "release")] + let flow = Flow::RoutingUnlinkConfig; + Box::pin(oss_api::server_wrap( + flow, + state, + &req, + payload.into_inner(), + |state, auth: auth::AuthenticationData, payload_req, _| { + routing::unlink_routing_config( + state, + auth.merchant_account, + payload_req, + transaction_type, + ) + }, + #[cfg(not(feature = "release"))] + auth::auth_type( + &auth::ApiKeyAuth, &auth::JWTAuth(Permission::RoutingWrite), - api_locking::LockAction::NotApplicable, - )) - .await - } + req.headers(), + ), + #[cfg(feature = "release")] + &auth::JWTAuth(Permission::RoutingWrite), + api_locking::LockAction::NotApplicable, + )) + .await } #[cfg(feature = "olap")] @@ -508,62 +445,35 @@ pub async fn retrieve_decision_manager_config( pub async fn routing_retrieve_linked_config( state: web::Data, req: HttpRequest, - #[cfg(feature = "business_profile_routing")] query: web::Query, - #[cfg(feature = "business_profile_routing")] transaction_type: &enums::TransactionType, + query: web::Query, + transaction_type: &enums::TransactionType, ) -> impl Responder { - #[cfg(feature = "business_profile_routing")] - { - use crate::services::authentication::AuthenticationData; - let flow = Flow::RoutingRetrieveActiveConfig; - Box::pin(oss_api::server_wrap( - flow, - state, - &req, - query.into_inner(), - |state, auth: AuthenticationData, query_params, _| { - routing::retrieve_linked_routing_config( - state, - auth.merchant_account, - query_params, - transaction_type, - ) - }, - #[cfg(not(feature = "release"))] - auth::auth_type( - &auth::ApiKeyAuth, - &auth::JWTAuth(Permission::RoutingRead), - req.headers(), - ), - #[cfg(feature = "release")] - &auth::JWTAuth(Permission::RoutingRead), - api_locking::LockAction::NotApplicable, - )) - .await - } - - #[cfg(not(feature = "business_profile_routing"))] - { - let flow = Flow::RoutingRetrieveActiveConfig; - Box::pin(oss_api::server_wrap( - flow, - state, - &req, - (), - |state, auth: auth::AuthenticationData, _, _| { - routing::retrieve_linked_routing_config(state, auth.merchant_account) - }, - #[cfg(not(feature = "release"))] - auth::auth_type( - &auth::ApiKeyAuth, - &auth::JWTAuth(Permission::RoutingRead), - req.headers(), - ), - #[cfg(feature = "release")] + use crate::services::authentication::AuthenticationData; + let flow = Flow::RoutingRetrieveActiveConfig; + Box::pin(oss_api::server_wrap( + flow, + state, + &req, + query.into_inner(), + |state, auth: AuthenticationData, query_params, _| { + routing::retrieve_linked_routing_config( + state, + auth.merchant_account, + query_params, + transaction_type, + ) + }, + #[cfg(not(feature = "release"))] + auth::auth_type( + &auth::ApiKeyAuth, &auth::JWTAuth(Permission::RoutingRead), - api_locking::LockAction::NotApplicable, - )) - .await - } + req.headers(), + ), + #[cfg(feature = "release")] + &auth::JWTAuth(Permission::RoutingRead), + api_locking::LockAction::NotApplicable, + )) + .await } #[cfg(feature = "olap")] diff --git a/crates/router/src/types/api/routing.rs b/crates/router/src/types/api/routing.rs index 4f982d543df2..d201f57eb924 100644 --- a/crates/router/src/types/api/routing.rs +++ b/crates/router/src/types/api/routing.rs @@ -1,11 +1,9 @@ -#[cfg(feature = "backwards_compatibility")] -pub use api_models::routing::RoutableChoiceKind; pub use api_models::{ enums as api_enums, routing::{ - ConnectorVolumeSplit, RoutableConnectorChoice, RoutingAlgorithm, RoutingAlgorithmKind, - RoutingAlgorithmRef, RoutingConfigRequest, RoutingDictionary, RoutingDictionaryRecord, - StraightThroughAlgorithm, + ConnectorVolumeSplit, RoutableChoiceKind, RoutableConnectorChoice, RoutingAlgorithm, + RoutingAlgorithmKind, RoutingAlgorithmRef, RoutingConfigRequest, RoutingDictionary, + RoutingDictionaryRecord, StraightThroughAlgorithm, }, }; @@ -13,8 +11,6 @@ use super::types::api as api_oss; pub struct SessionRoutingChoice { pub connector: api_oss::ConnectorData, - #[cfg(not(feature = "connector_choice_mca_id"))] - pub sub_label: Option, pub payment_method_type: api_enums::PaymentMethodType, } diff --git a/crates/router/src/types/storage.rs b/crates/router/src/types/storage.rs index 7713d88cb429..b12457984446 100644 --- a/crates/router/src/types/storage.rs +++ b/crates/router/src/types/storage.rs @@ -71,10 +71,9 @@ use crate::types::api::routing; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct RoutingData { pub routed_through: Option, - #[cfg(feature = "connector_choice_mca_id")] + pub merchant_connector_id: Option, - #[cfg(not(feature = "connector_choice_mca_id"))] - pub business_sub_label: Option, + pub routing_info: PaymentRoutingInfo, pub algorithm: Option, } diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/request.json index 0915e9894bb6..f8e5000d3144 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Happy Cases/Scenario22-Create Gift Card payment/Payments - Create/request.json @@ -42,10 +42,6 @@ } } }, - "routing": { - "type": "single", - "data": "adyen" - }, "billing": { "address": { "line1": "1467", diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json index ed9dbeaa9c49..8ac3ed14b0a7 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/QuickStart/Payments - Create/request.json @@ -43,10 +43,6 @@ "card_cvc": "7373" } }, - "routing": { - "type": "single", - "data": "adyen" - }, "billing": { "address": { "line1": "1467", diff --git a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json index 11437ff57659..923cb4aae78b 100644 --- a/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json +++ b/postman/collection-dir/adyen_uk/Flow Testcases/Variation Cases/Scenario10-Create Gift Card payment where it fails due to insufficient balance/Payments - Create/request.json @@ -42,10 +42,6 @@ } } }, - "routing": { - "type": "single", - "data": "adyen" - }, "billing": { "address": { "line1": "1467", diff --git a/postman/collection-dir/authorizedotnet/Flow Testcases/Happy Cases/Scenario4-Create failed payment with confirm true copy/Payments - Create/request.json b/postman/collection-dir/authorizedotnet/Flow Testcases/Happy Cases/Scenario4-Create failed payment with confirm true copy/Payments - Create/request.json index b9bf4f2b66ea..f477cdecc3f8 100644 --- a/postman/collection-dir/authorizedotnet/Flow Testcases/Happy Cases/Scenario4-Create failed payment with confirm true copy/Payments - Create/request.json +++ b/postman/collection-dir/authorizedotnet/Flow Testcases/Happy Cases/Scenario4-Create failed payment with confirm true copy/Payments - Create/request.json @@ -16,10 +16,6 @@ "amount": 7003, "currency": "USD", "confirm": true, - "routing": { - "data": "authorizedotnet", - "type": "single" - }, "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "customer_id": "StripeCustomer", diff --git a/postman/collection-dir/authorizedotnet/Flow Testcases/Variation Cases/Scenario12-Failed case for wrong api keys/Payments - Create/request.json b/postman/collection-dir/authorizedotnet/Flow Testcases/Variation Cases/Scenario12-Failed case for wrong api keys/Payments - Create/request.json index b9bf4f2b66ea..f477cdecc3f8 100644 --- a/postman/collection-dir/authorizedotnet/Flow Testcases/Variation Cases/Scenario12-Failed case for wrong api keys/Payments - Create/request.json +++ b/postman/collection-dir/authorizedotnet/Flow Testcases/Variation Cases/Scenario12-Failed case for wrong api keys/Payments - Create/request.json @@ -16,10 +16,6 @@ "amount": 7003, "currency": "USD", "confirm": true, - "routing": { - "data": "authorizedotnet", - "type": "single" - }, "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "customer_id": "StripeCustomer", diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json index 43eb0c9cc123..cc19ac2bab29 100644 --- a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json @@ -86,10 +86,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json index c2a8a615d1c2..14f671d7f6fd 100644 --- a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json @@ -78,10 +78,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json index 71cc91069581..66f5c825b122 100644 --- a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json @@ -64,10 +64,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "bankofamerica" } } }, diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json index 8bd119ed2fea..5ec6335d05ab 100644 --- a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json @@ -86,10 +86,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json index 8ea0f5f20392..b2c86ceb5e70 100644 --- a/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json +++ b/postman/collection-dir/bankofamerica/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json @@ -86,10 +86,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/request.json index 580e722c1d5d..2685dca3f068 100644 --- a/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/bankofamerica/Flow Testcases/QuickStart/Payments - Create/request.json @@ -83,10 +83,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "bankofamerica" } } }, diff --git a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario28-Create partially captured payment with refund/Payments - Create/request.json b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario28-Create partially captured payment with refund/Payments - Create/request.json index 4cbb79aa56af..b4169de20837 100644 --- a/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario28-Create partially captured payment with refund/Payments - Create/request.json +++ b/postman/collection-dir/bluesnap/Flow Testcases/Happy Cases/Scenario28-Create partially captured payment with refund/Payments - Create/request.json @@ -74,10 +74,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/3DS Payment/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/3DS Payment/Payments - Create/request.json index 197580ddffa4..74441099ea1b 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/3DS Payment/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/3DS Payment/Payments - Create/request.json @@ -71,10 +71,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "checkout" } } }, diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Cancel After Partial Capture/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Cancel After Partial Capture/Payments - Create/request.json index b28dec99902e..04df55714a78 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Cancel After Partial Capture/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Cancel After Partial Capture/Payments - Create/request.json @@ -71,10 +71,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "checkout" } } }, diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Refund After Partial Capture/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Refund After Partial Capture/Payments - Create/request.json index b28dec99902e..04df55714a78 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Refund After Partial Capture/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Refund After Partial Capture/Payments - Create/request.json @@ -71,10 +71,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "checkout" } } }, diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Retrieve After Partial Capture/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Retrieve After Partial Capture/Payments - Create/request.json index b28dec99902e..04df55714a78 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Retrieve After Partial Capture/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Retrieve After Partial Capture/Payments - Create/request.json @@ -71,10 +71,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "checkout" } } }, diff --git a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Successful Partial Capture and Refund/Payments - Create/request.json b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Successful Partial Capture and Refund/Payments - Create/request.json index b28dec99902e..04df55714a78 100644 --- a/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Successful Partial Capture and Refund/Payments - Create/request.json +++ b/postman/collection-dir/checkout/Flow Testcases/Happy Cases/Scenario10-Multiple Captures/Successful Partial Capture and Refund/Payments - Create/request.json @@ -71,10 +71,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "checkout" } } }, diff --git a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Save card payments - Create/request.json b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Save card payments - Create/request.json index 4afeccc8246d..f40a645ea0fc 100644 --- a/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Save card payments - Create/request.json +++ b/postman/collection-dir/cybersource/Flow Testcases/Happy Cases/Scenario9-Add card flow/Save card payments - Create/request.json @@ -66,10 +66,6 @@ "country_code": "+91" } }, - "routing": { - "type": "single", - "data": "stripe" - }, "statement_descriptor_name": "joseph", "statement_descriptor_suffix": "JS", "metadata": { diff --git a/postman/collection-dir/forte/Flow Testcases/Happy Cases/Scenario7-Create payment with Zero Amount/Payments - Create/request.json b/postman/collection-dir/forte/Flow Testcases/Happy Cases/Scenario7-Create payment with Zero Amount/Payments - Create/request.json index 0bf23604d848..624968c77cd9 100644 --- a/postman/collection-dir/forte/Flow Testcases/Happy Cases/Scenario7-Create payment with Zero Amount/Payments - Create/request.json +++ b/postman/collection-dir/forte/Flow Testcases/Happy Cases/Scenario7-Create payment with Zero Amount/Payments - Create/request.json @@ -30,10 +30,6 @@ "phone_country_code": "+65", "description": "Its my first payment request", "authentication_type": "no_three_ds", - "routing": { - "type": "single", - "data": "forte" - }, "return_url": "https://duck.com", "payment_method": "card", "payment_method_data": { diff --git a/postman/collection-dir/forte/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/forte/Flow Testcases/QuickStart/Payments - Create/request.json index 04be684704b6..05fde18c2d97 100644 --- a/postman/collection-dir/forte/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/forte/Flow Testcases/QuickStart/Payments - Create/request.json @@ -21,10 +21,6 @@ "amount": 6540, "currency": "USD", "confirm": true, - "routing": { - "type": "single", - "data": "forte" - }, "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, diff --git a/postman/collection-dir/hyperswitch/Hackathon/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json b/postman/collection-dir/hyperswitch/Hackathon/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json index 64df182d1e7b..d1361c52bc75 100644 --- a/postman/collection-dir/hyperswitch/Hackathon/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json +++ b/postman/collection-dir/hyperswitch/Hackathon/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json @@ -21,10 +21,6 @@ "amount": 8000, "currency": "USD", "confirm": false, - "routing": { - "type": "single", - "data": "stripe" - }, "capture_method": "automatic", "capture_on": "2022-09-10T10:11:12Z", "amount_to_capture": 6540, diff --git a/postman/collection-dir/multisafepay/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/multisafepay/Flow Testcases/QuickStart/Payments - Create/request.json index a4e816ad17d2..289e780a72cb 100644 --- a/postman/collection-dir/multisafepay/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/multisafepay/Flow Testcases/QuickStart/Payments - Create/request.json @@ -55,10 +55,6 @@ "last_name": "happy" } }, - "routing": { - "type": "single", - "data": "multisafepay" - }, "shipping": { "address": { "line1": "1467", diff --git a/postman/collection-dir/multisafepay/Flow Testcases/Variation Cases/Scenario6- Create payment with Invalid Merchant ID/Payments - Create/request.json b/postman/collection-dir/multisafepay/Flow Testcases/Variation Cases/Scenario6- Create payment with Invalid Merchant ID/Payments - Create/request.json index 525eaa739e83..289e780a72cb 100644 --- a/postman/collection-dir/multisafepay/Flow Testcases/Variation Cases/Scenario6- Create payment with Invalid Merchant ID/Payments - Create/request.json +++ b/postman/collection-dir/multisafepay/Flow Testcases/Variation Cases/Scenario6- Create payment with Invalid Merchant ID/Payments - Create/request.json @@ -42,10 +42,6 @@ "card_cvc": "123" } }, - "routing": { - "type": "single", - "data": "multisafepay" - }, "billing": { "address": { "line1": "1467", diff --git a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario13-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario13-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json index 050f353af538..e04dc7b4280f 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario13-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json +++ b/postman/collection-dir/nmi/Flow Testcases/Happy Cases/Scenario13-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json @@ -86,10 +86,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json b/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json index 07a9e0fc0b95..db81c387d5a7 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json +++ b/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario9-Create a mandate payment/Payments - Create/request.json b/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario9-Create a mandate payment/Payments - Create/request.json index 2cbfe4a20f1e..314752373cc8 100644 --- a/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario9-Create a mandate payment/Payments - Create/request.json +++ b/postman/collection-dir/nmi/Flow Testcases/Variation Cases/Scenario9-Create a mandate payment/Payments - Create/request.json @@ -97,10 +97,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json index 9e084a35c8c9..691f63896f6a 100644 --- a/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json +++ b/postman/collection-dir/paypal/Flow Testcases/Happy Cases/Scenario8-Create payment with Manual capture with confirm false and surcharge_data/Payments - Create/request.json @@ -65,10 +65,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "paypal" } } }, diff --git a/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json b/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json index 2c8566c24282..58e786c79dbf 100644 --- a/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json +++ b/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json @@ -28,13 +28,9 @@ "payment_method": "card_redirect", "payment_method_type": "card_redirect", "payment_method_data": { - "card_redirect": { - "card_redirect": {} - } - }, - "routing": { - "type": "single", - "data": "prophetpay" + "card_redirect": { + "card_redirect": {} + } } } }, diff --git a/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json b/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json index 851a18115f97..514099b88a1a 100644 --- a/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json +++ b/postman/collection-dir/prophetpay/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json @@ -24,11 +24,7 @@ "amount_to_capture": 8000, "business_country": "US", "customer_id": "not_a_rick_roll", - "return_url": "https://www.google.com", - "routing": { - "type": "single", - "data": "prophetpay" - } + "return_url": "https://www.google.com" } }, "url": { diff --git a/postman/collection-dir/prophetpay/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/prophetpay/Flow Testcases/QuickStart/Payments - Create/request.json index 11c9749e9589..5302bca0e280 100644 --- a/postman/collection-dir/prophetpay/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/prophetpay/Flow Testcases/QuickStart/Payments - Create/request.json @@ -28,13 +28,9 @@ "payment_method": "card_redirect", "payment_method_type": "card_redirect", "payment_method_data": { - "card_redirect": { - "card_redirect": {} - } - }, - "routing": { - "type": "single", - "data": "prophetpay" + "card_redirect": { + "card_redirect": {} + } } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json index 950ec531ae7c..48b7d410897f 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json @@ -98,10 +98,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/request.json index b5ef6b1afe21..b32011d26dc8 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Payments - Create/request.json @@ -97,10 +97,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/request.json index 613e9148f787..f6038bfc21bf 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario10-Create a mandate and recurring payment/Recurring Payments - Create/request.json @@ -52,10 +52,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Payments - Create/request.json index 61f007ede682..18f12b7306d4 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Payments - Create/request.json @@ -85,10 +85,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Recurring Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Recurring Payments - Create/request.json index fe8a73d4581a..8ca8a333ad34 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Recurring Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario11-Refund recurring payment/Recurring Payments - Create/request.json @@ -52,10 +52,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json index f621bd52f00d..88e6597536af 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario12-BNPL-klarna/Payments - Create/request.json @@ -38,10 +38,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json index d108e1198a7d..2e536713b9fb 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario13-BNPL-afterpay/Payments - Create/request.json @@ -78,10 +78,6 @@ "amount": 7000, "quantity": 1 } - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario14-BNPL-affirm/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario14-BNPL-affirm/Payments - Create/request.json index eedc71f3a1aa..3675475c0837 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario14-BNPL-affirm/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario14-BNPL-affirm/Payments - Create/request.json @@ -77,10 +77,6 @@ "amount": 7000, "quantity": 1 } - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-Ideal/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-Ideal/Payments - Create/request.json index 048893f35111..247f1256f7ab 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-Ideal/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario15-Bank Redirect-Ideal/Payments - Create/request.json @@ -76,10 +76,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-sofort/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-sofort/Payments - Create/request.json index 21e71ad037a9..0b0c56d26601 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-sofort/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario16-Bank Redirect-sofort/Payments - Create/request.json @@ -76,10 +76,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario17-Bank Redirect-eps/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario17-Bank Redirect-eps/Payments - Create/request.json index 026af8449ce8..f423db211509 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario17-Bank Redirect-eps/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario17-Bank Redirect-eps/Payments - Create/request.json @@ -76,10 +76,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario18-Bank Transfer-ach/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario18-Bank Transfer-ach/Payments - Create/request.json index 7fa60b50cda7..b9e58c09ce9a 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario18-Bank Transfer-ach/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario18-Bank Transfer-ach/Payments - Create/request.json @@ -52,10 +52,6 @@ "last_name": "Kumari" }, "email": "guest@example.com" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario19-Bank Debit-ach/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario19-Bank Debit-ach/Payments - Create/request.json index 53fe9371e492..473970b21c61 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario19-Bank Debit-ach/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario19-Bank Debit-ach/Payments - Create/request.json @@ -95,10 +95,6 @@ "amount": 1800, "account_name": "transaction_processing" } - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Wallet-Wechatpay/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Wallet-Wechatpay/Payments - Create/request.json index daffd2ab9ec1..2b623eed2ed7 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Wallet-Wechatpay/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario20-Wallet-Wechatpay/Payments - Create/request.json @@ -40,10 +40,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario21- Update address and List Payment method/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario21- Update address and List Payment method/Payments - Create/request.json index beeb5b3983f5..2f7bdae8a6af 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario21- Update address and List Payment method/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario21- Update address and List Payment method/Payments - Create/request.json @@ -62,10 +62,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario22- Update Amount/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario22- Update Amount/Payments - Create/request.json index 785122a83c50..b41aa02e5d0a 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario22- Update Amount/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario22- Update Amount/Payments - Create/request.json @@ -62,10 +62,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Payments - Create/request.json index 9d931d385303..4f64a5d0e343 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Payments - Create/request.json @@ -86,10 +86,6 @@ "country_code": "+91" } }, - "routing": { - "type": "single", - "data": "stripe" - }, "statement_descriptor_name": "joseph", "statement_descriptor_suffix": "JS", "metadata": { diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Save card payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Save card payments - Create/request.json index 4afeccc8246d..3371960e6720 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Save card payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario23-Add card flow/Save card payments - Create/request.json @@ -66,10 +66,6 @@ "country_code": "+91" } }, - "routing": { - "type": "single", - "data": "stripe" - }, "statement_descriptor_name": "joseph", "statement_descriptor_suffix": "JS", "metadata": { @@ -81,12 +77,8 @@ }, "url": { "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] + "host": ["{{baseUrl}}"], + "path": ["payments"] }, "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" } diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json index b903a3ca741b..b0ff74e8884f 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Payments - Create/request.json @@ -92,10 +92,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Save card payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Save card payments - Create/request.json index a3ca3aa6ac9c..f40a645ea0fc 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Save card payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario24-Don't Pass CVV for save card flow and verifysuccess payment/Save card payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Payments - Create/request.json index 9b1092f93fc3..7b15b0ee8714 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Payments - Create/request.json @@ -92,10 +92,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Save card payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Save card payments - Create/request.json index dc78d553ec53..0e614b315f9e 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Save card payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario25-Save card payment with manual capture/Save card payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json index 6a9836d232cc..627c816fba92 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario26-Create payment without customer_id and with billing address and shipping address/Payments - Create/request.json @@ -86,10 +86,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario27-Confirm a payment with requires_customer_action status/Payments - Create with confirm true/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario27-Confirm a payment with requires_customer_action status/Payments - Create with confirm true/request.json index 7e28058ccf62..2fc1d3e06265 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario27-Confirm a payment with requires_customer_action status/Payments - Create with confirm true/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario27-Confirm a payment with requires_customer_action status/Payments - Create with confirm true/request.json @@ -83,10 +83,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario28-Create payment with payment method billing/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario28-Create payment with payment method billing/Payments - Create/request.json index 83e3a58da2e0..578d3c472672 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario28-Create payment with payment method billing/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario28-Create payment with payment method billing/Payments - Create/request.json @@ -79,10 +79,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario29-Update payment with payment method billing/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario29-Update payment with payment method billing/Payments - Create/request.json index 95c2541e0bca..14381e2d55a9 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario29-Update payment with payment method billing/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario29-Update payment with payment method billing/Payments - Create/request.json @@ -54,10 +54,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json index beeb5b3983f5..2f7bdae8a6af 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json @@ -62,10 +62,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json index 0619498e38c7..4deef11deb58 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4-Create payment with Manual capture/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4a-Create payment with manual_multiple capture/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4a-Create payment with manual_multiple capture/Payments - Create/request.json index 0fbd6a4dcdd5..f1ff4cb5692e 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4a-Create payment with manual_multiple capture/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario4a-Create payment with manual_multiple capture/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json index 0619498e38c7..4deef11deb58 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario5-Void the payment/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario6-Create 3DS payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario6-Create 3DS payment/Payments - Create/request.json index 7e28058ccf62..2fc1d3e06265 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario6-Create 3DS payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario6-Create 3DS payment/Payments - Create/request.json @@ -83,10 +83,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario7-Create 3DS payment with confirm false/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario7-Create 3DS payment with confirm false/Payments - Create/request.json index 14f3394596fd..72c0cf1fd9b2 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario7-Create 3DS payment with confirm false/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario7-Create 3DS payment with confirm false/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario8-Create a failure card payment with confirm true/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario8-Create a failure card payment with confirm true/Payments - Create/request.json index a3e0f52b8d77..1a9dc985891c 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario8-Create a failure card payment with confirm true/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario8-Create a failure card payment with confirm true/Payments - Create/request.json @@ -86,10 +86,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/request.json index 2363c62ff27f..f58c425653ca 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9-Refund full payment/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9a-Partial refund/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9a-Partial refund/Payments - Create/request.json index b5f464abc14b..f58c425653ca 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9a-Partial refund/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Happy Cases/Scenario9a-Partial refund/Payments - Create/request.json @@ -72,21 +72,13 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, "url": { "raw": "{{baseUrl}}/payments", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "payments" - ] + "host": ["{{baseUrl}}"], + "path": ["payments"] }, "description": "To process a payment you will have to create a payment, attach a payment method and confirm. Depending on the user journey you wish to achieve, you may opt to all the steps in a single request or in a sequence of API request using following APIs: (i) Payments - Update, (ii) Payments - Confirm, and (iii) Payments - Capture" } diff --git a/postman/collection-dir/stripe/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/QuickStart/Payments - Create/request.json index 0a7b19679aa6..2225c6096e37 100644 --- a/postman/collection-dir/stripe/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/QuickStart/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json index 0e1f282acdb9..02e431466aa2 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp Year)/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json index 92e81f4f53ae..52c73bea7433 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid Exp month)/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json index a8f4cf69a292..c7ad4cb2749f 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(Invalid card number)/request.json @@ -70,10 +70,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json index fdc09ce202f9..9fa8e3bbe129 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario1-Create payment with Invalid card details/Payments - Create(invalid CVV)/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario10-Refund exceeds amount captured/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario10-Refund exceeds amount captured/Payments - Create/request.json index 90b6e3bd0385..d6f53f92a041 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario10-Refund exceeds amount captured/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario10-Refund exceeds amount captured/Payments - Create/request.json @@ -71,10 +71,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Create/request.json index beeb5b3983f5..2f7bdae8a6af 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario2-Confirming the payment without PMD/Payments - Create/request.json @@ -62,10 +62,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json index 0619498e38c7..4deef11deb58 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario3-Capture greater amount/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json index 2363c62ff27f..f58c425653ca 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario4-Capture the succeeded payment/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json index 2363c62ff27f..f58c425653ca 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario5-Void the success_slash_failure payment/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario6-Create 3DS payment with greater capture/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario6-Create 3DS payment with greater capture/Payments - Create/request.json index c132368eb406..9c2ddbffb6d7 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario6-Create 3DS payment with greater capture/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario6-Create 3DS payment with greater capture/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json index 2363c62ff27f..f58c425653ca 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario7-Refund exceeds amount/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json index 4105bd1a869b..3e1f1231ff86 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario8-Refund for unsuccessful payment/Payments - Create/request.json @@ -72,10 +72,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json index b5ef6b1afe21..b32011d26dc8 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Payments - Create/request.json @@ -97,10 +97,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Recurring Payments - Create/request.json b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Recurring Payments - Create/request.json index 3ced4fa53275..299eb1685b71 100644 --- a/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Recurring Payments - Create/request.json +++ b/postman/collection-dir/stripe/Flow Testcases/Variation Cases/Scenario9-Create a recurring payment with greater mandate amount/Recurring Payments - Create/request.json @@ -64,10 +64,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/PaymentMethods/Payments - Create/request.json b/postman/collection-dir/stripe/PaymentMethods/Payments - Create/request.json index 3b16fb5e6598..93bda6402292 100644 --- a/postman/collection-dir/stripe/PaymentMethods/Payments - Create/request.json +++ b/postman/collection-dir/stripe/PaymentMethods/Payments - Create/request.json @@ -92,10 +92,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Payments/Payments - Create Again/request.json b/postman/collection-dir/stripe/Payments/Payments - Create Again/request.json index bef68a26086d..c985bf50b0de 100644 --- a/postman/collection-dir/stripe/Payments/Payments - Create Again/request.json +++ b/postman/collection-dir/stripe/Payments/Payments - Create Again/request.json @@ -83,10 +83,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Payments/Payments - Create Yet Again/request.json b/postman/collection-dir/stripe/Payments/Payments - Create Yet Again/request.json index 544af8234f60..2b6d4bf3338d 100644 --- a/postman/collection-dir/stripe/Payments/Payments - Create Yet Again/request.json +++ b/postman/collection-dir/stripe/Payments/Payments - Create Yet Again/request.json @@ -83,10 +83,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/Payments/Payments - Create/request.json b/postman/collection-dir/stripe/Payments/Payments - Create/request.json index 55ddd525d4a6..5a7457cedf96 100644 --- a/postman/collection-dir/stripe/Payments/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Payments/Payments - Create/request.json @@ -83,10 +83,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/stripe/QuickStart/Payments - Create-copy/request.json b/postman/collection-dir/stripe/QuickStart/Payments - Create-copy/request.json index 34d455fe769e..5fcc5647594d 100644 --- a/postman/collection-dir/stripe/QuickStart/Payments - Create-copy/request.json +++ b/postman/collection-dir/stripe/QuickStart/Payments - Create-copy/request.json @@ -86,7 +86,10 @@ }, "routing": { "type": "single", - "data": "stripe" + "data": { + "connector":"stripe", + "merchant_connector_id":"{{merchant_connector_id}}" + } } } }, diff --git a/postman/collection-dir/stripe/QuickStart/Payments - Create/request.json b/postman/collection-dir/stripe/QuickStart/Payments - Create/request.json index cbf58f2e89ae..13ed1a212de4 100644 --- a/postman/collection-dir/stripe/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/stripe/QuickStart/Payments - Create/request.json @@ -87,7 +87,10 @@ }, "routing": { "type": "single", - "data": "stripe" + "data": { + "connector":"stripe", + "merchant_connector_id":"{{merchant_connector_id}}" + } } } }, diff --git a/postman/collection-dir/stripe/Refunds/Payments - Create/request.json b/postman/collection-dir/stripe/Refunds/Payments - Create/request.json index 24a37d16cf54..d3219a30cf88 100644 --- a/postman/collection-dir/stripe/Refunds/Payments - Create/request.json +++ b/postman/collection-dir/stripe/Refunds/Payments - Create/request.json @@ -84,10 +84,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "stripe" } } }, diff --git a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json index bd076535780a..6cf802f630c0 100644 --- a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json +++ b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario1-Create payment with confirm true/Payments - Create/request.json @@ -82,10 +82,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "volt" } } }, diff --git a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json index 744663ee79e1..b5bbc7aca19f 100644 --- a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json +++ b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario2-Create payment with confirm false/Payments - Create/request.json @@ -82,10 +82,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "volt" } } }, diff --git a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json index 7323f440e645..bde98e7ef082 100644 --- a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json +++ b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario3-Create payment without PMD/Payments - Create/request.json @@ -63,10 +63,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "volt" } } }, diff --git a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario4-Bank Redirect-open_banking_uk/Payments - Create/request.json b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario4-Bank Redirect-open_banking_uk/Payments - Create/request.json index 7323f440e645..bde98e7ef082 100644 --- a/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario4-Bank Redirect-open_banking_uk/Payments - Create/request.json +++ b/postman/collection-dir/volt/Flow Testcases/Happy Cases/Scenario4-Bank Redirect-open_banking_uk/Payments - Create/request.json @@ -63,10 +63,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "volt" } } }, diff --git a/postman/collection-dir/volt/Flow Testcases/QuickStart/Payments - Create/request.json b/postman/collection-dir/volt/Flow Testcases/QuickStart/Payments - Create/request.json index bd076535780a..6cf802f630c0 100644 --- a/postman/collection-dir/volt/Flow Testcases/QuickStart/Payments - Create/request.json +++ b/postman/collection-dir/volt/Flow Testcases/QuickStart/Payments - Create/request.json @@ -82,10 +82,6 @@ "udf1": "value1", "new_customer": "true", "login_date": "2019-09-10T10:11:12Z" - }, - "routing": { - "type": "single", - "data": "volt" } } }, From edb581e8e3307714c1ebc3dec65fc73aeb2d7cb0 Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Thu, 18 Jul 2024 16:35:54 +0530 Subject: [PATCH 14/26] chore: Increasing log coverage for payment method list (#5042) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- crates/router/src/core/payment_methods/cards.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 5a4d40ef85ee..468bc0dbfc3c 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -2424,6 +2424,10 @@ pub async fn list_payment_methods( .await?; } } + logger::info!( + "The Payment Methods available after Constraint Graph filtering are {:?}", + response + ); // Filter out wallet payment method from mca if customer has already saved it customer @@ -3482,6 +3486,7 @@ pub async fn filter_payment_methods( .unwrap_or(true); let context = AnalysisContext::from_dir_values(context_values.clone()); + logger::info!("Context created for List Payment method is {:?}", context); let domain_ident: &[String] = &[mca_id.clone()]; let result = graph.key_value_analysis( @@ -3491,7 +3496,13 @@ pub async fn filter_payment_methods( &mut cgraph::CycleCheck::new(), Some(domain_ident), ); - if filter_pm_based_on_allowed_types + if let Err(ref e) = result { + logger::error!( + "Error while performing Constraint graph's key value analysis + for list payment methods {:?}", + e + ); + } else if filter_pm_based_on_allowed_types && filter_pm_card_network_based && saved_payment_methods_filter && matches!(result, Ok(())) From 93047ae616b6a5240c2be13f8c5267e5fa616eab Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Thu, 18 Jul 2024 18:14:19 +0530 Subject: [PATCH 15/26] refactor(blocklist): change primary key of blocklist table (#5356) --- crates/diesel_models/src/schema.rs | 2 +- crates/diesel_models/src/schema_v2.rs | 2 +- .../down.sql | 5 +++++ .../up.sql | 12 ++++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/down.sql create mode 100644 migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/up.sql diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index b27d1e9611fd..171e1cfe2e34 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -128,7 +128,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - blocklist (id) { + blocklist (merchant_id, fingerprint_id) { id -> Int4, #[max_length = 64] merchant_id -> Varchar, diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index 3fdbf45025fc..d16ddcd65181 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -128,7 +128,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - blocklist (id) { + blocklist (merchant_id, fingerprint_id) { id -> Int4, #[max_length = 64] merchant_id -> Varchar, diff --git a/migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/down.sql b/migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/down.sql new file mode 100644 index 000000000000..9600ce5addfe --- /dev/null +++ b/migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE blocklist DROP CONSTRAINT blocklist_pkey; + +ALTER TABLE blocklist +ADD PRIMARY KEY (id); diff --git a/migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/up.sql b/migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/up.sql new file mode 100644 index 000000000000..c0df6d6345cb --- /dev/null +++ b/migrations/2024-07-17-174449_change_primary_key_for_blocklist_table/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here +-- The below query will lock the blocklist table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query should be run after the new version of application is deployed +ALTER TABLE blocklist DROP CONSTRAINT blocklist_pkey; + +-- Use the `merchant_id, fingerprint_id` columns as primary key +-- These are already unique, not null columns +-- So this query should not fail for not null or duplicate value reasons +ALTER TABLE blocklist +ADD PRIMARY KEY (merchant_id, fingerprint_id); From 7f582e4737c1c7dfe906e7d01de239e131511f84 Mon Sep 17 00:00:00 2001 From: Sanchith Hegde <22217505+SanchithHegde@users.noreply.github.com> Date: Thu, 18 Jul 2024 18:51:42 +0530 Subject: [PATCH 16/26] build: remove unused dependencies (#5343) --- Cargo.lock | 75 ------------------- crates/analytics/Cargo.toml | 31 ++------ crates/api_models/Cargo.toml | 3 +- crates/api_models/src/enums.rs | 1 - crates/common_enums/Cargo.toml | 4 +- crates/common_enums/src/enums.rs | 1 - crates/common_utils/Cargo.toml | 7 +- crates/connector_configs/Cargo.toml | 3 + crates/currency_conversion/Cargo.toml | 1 - crates/diesel_models/Cargo.toml | 2 - crates/diesel_models/src/enums.rs | 4 - crates/drainer/Cargo.toml | 2 +- crates/euclid/Cargo.toml | 7 +- crates/euclid_wasm/Cargo.toml | 7 +- crates/events/Cargo.toml | 4 +- crates/external_services/Cargo.toml | 8 +- crates/hsdev/Cargo.toml | 8 +- .../hyperswitch_constraint_graph/Cargo.toml | 3 - crates/hyperswitch_domain_models/Cargo.toml | 6 +- crates/hyperswitch_interfaces/Cargo.toml | 1 - crates/kgraph_utils/Cargo.toml | 6 +- crates/masking/Cargo.toml | 2 +- crates/openapi/Cargo.toml | 4 +- crates/pm_auth/Cargo.toml | 2 - crates/redis_interface/Cargo.toml | 2 +- crates/router/Cargo.toml | 40 +++++----- crates/router_derive/Cargo.toml | 2 +- crates/router_env/Cargo.toml | 1 - crates/scheduler/Cargo.toml | 7 +- crates/storage_impl/Cargo.toml | 8 +- crates/test_utils/Cargo.toml | 2 +- 31 files changed, 64 insertions(+), 190 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0188b3d6f1c1..193f19453b9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -344,7 +344,6 @@ dependencies = [ "common_utils", "diesel_models", "error-stack", - "external_services", "futures 0.3.30", "hyperswitch_domain_models", "hyperswitch_interfaces", @@ -449,8 +448,6 @@ dependencies = [ "common_utils", "error-stack", "euclid", - "frunk", - "frunk_core", "masking", "mime", "reqwest", @@ -1996,8 +1993,6 @@ name = "common_enums" version = "0.1.0" dependencies = [ "diesel", - "frunk", - "frunk_core", "router_derive", "serde", "serde_json", @@ -2032,11 +2027,9 @@ dependencies = [ "regex", "reqwest", "ring 0.17.8", - "router_derive", "router_env", "rust_decimal", "rustc-hash", - "rusty-money", "semver 1.0.22", "serde", "serde_json", @@ -2708,8 +2701,6 @@ dependencies = [ "common_utils", "diesel", "error-stack", - "frunk", - "frunk_core", "masking", "router_derive", "router_env", @@ -2966,10 +2957,7 @@ dependencies = [ "common_enums", "common_utils", "criterion", - "erased-serde 0.4.4", "euclid_macros", - "frunk", - "frunk_core", "hyperswitch_constraint_graph", "nom", "once_cell", @@ -3225,58 +3213,6 @@ dependencies = [ "urlencoding", ] -[[package]] -name = "frunk" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a351b59e12f97b4176ee78497dff72e4276fb1ceb13e19056aca7fa0206287" -dependencies = [ - "frunk_core", - "frunk_derives", - "frunk_proc_macros", -] - -[[package]] -name = "frunk_core" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2469fab0bd07e64ccf0ad57a1438f63160c69b2e57f04a439653d68eb558d6" - -[[package]] -name = "frunk_derives" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fa992f1656e1707946bbba340ad244f0814009ef8c0118eb7b658395f19a2e" -dependencies = [ - "frunk_proc_macro_helpers", - "quote", - "syn 2.0.57", -] - -[[package]] -name = "frunk_proc_macro_helpers" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35b54add839292b743aeda6ebedbd8b11e93404f902c56223e51b9ec18a13d2c" -dependencies = [ - "frunk_core", - "proc-macro2", - "quote", - "syn 2.0.57", -] - -[[package]] -name = "frunk_proc_macros" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b85a1d4a9a6b300b41c05e8e13ef2feca03e0334127f29eca9506a7fe13a93" -dependencies = [ - "frunk_core", - "frunk_proc_macro_helpers", - "quote", - "syn 2.0.57", -] - [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -3900,7 +3836,6 @@ dependencies = [ "graphviz-rust", "rustc-hash", "serde", - "serde_json", "strum 0.25.0", "thiserror", ] @@ -3954,7 +3889,6 @@ dependencies = [ "router_env", "serde", "serde_json", - "storage_impl", "thiserror", "time", ] @@ -5483,8 +5417,6 @@ dependencies = [ "http 0.2.12", "masking", "mime", - "router_derive", - "router_env", "serde", "serde_json", "strum 0.26.2", @@ -6123,7 +6055,6 @@ dependencies = [ "digest", "dyn-clone", "encoding_rs", - "erased-serde 0.4.4", "error-stack", "euclid", "events", @@ -6179,7 +6110,6 @@ dependencies = [ "serde_with", "serial_test", "sha1", - "sqlx", "storage_impl", "strum 0.26.2", "tera", @@ -6221,7 +6151,6 @@ name = "router_env" version = "0.1.0" dependencies = [ "cargo_metadata 0.18.1", - "common_enums", "config", "error-stack", "gethostname", @@ -6548,7 +6477,6 @@ dependencies = [ "external_services", "futures 0.3.30", "hyperswitch_domain_models", - "masking", "num_cpus", "once_cell", "rand", @@ -7235,7 +7163,6 @@ dependencies = [ name = "storage_impl" version = "0.1.0" dependencies = [ - "actix-web", "api_models", "async-bb8-diesel", "async-trait", @@ -7250,10 +7177,8 @@ dependencies = [ "dyn-clone", "error-stack", "futures 0.3.30", - "http 0.2.12", "hyperswitch_domain_models", "masking", - "mime", "moka", "once_cell", "redis_interface", diff --git a/crates/analytics/Cargo.toml b/crates/analytics/Cargo.toml index 8cf4b9b911cb..9ef0e624fc24 100644 --- a/crates/analytics/Cargo.toml +++ b/crates/analytics/Cargo.toml @@ -6,28 +6,17 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - [dependencies] # First party crates -api_models = { version = "0.1.0", path = "../api_models", features = [ - "errors", -] } -storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false } +api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils" } -external_services = { version = "0.1.0", path = "../external_services", default-features = false } +diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } +hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } hyperswitch_interfaces = { version = "0.1.0", path = "../hyperswitch_interfaces" } masking = { version = "0.1.0", path = "../masking" } -router_env = { version = "0.1.0", path = "../router_env", features = [ - "log_extra_implicit_fields", - "log_custom_entries_to_extra", -] } -diesel_models = { version = "0.1.0", path = "../diesel_models", features = [ - "kv_store", -] } -hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } +router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } +storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false } #Third Party dependencies actix-web = "4.5.1" @@ -38,18 +27,12 @@ aws-smithy-types = { version = "1.1.8" } bigdecimal = { version = "0.3.1", features = ["serde"] } error-stack = "0.4.1" futures = "0.3.30" -opensearch = { version = "2.2.0", features = ["aws-auth"] } once_cell = "1.19.0" +opensearch = { version = "2.2.0", features = ["aws-auth"] } reqwest = { version = "0.11.27", features = ["serde_json"] } serde = { version = "1.0.197", features = ["derive", "rc"] } serde_json = "1.0.115" -sqlx = { version = "0.7.3", features = [ - "postgres", - "runtime-tokio", - "runtime-tokio-native-tls", - "time", - "bigdecimal", -] } +sqlx = { version = "0.7.3", features = ["postgres", "runtime-tokio", "runtime-tokio-native-tls", "time", "bigdecimal"] } strum = { version = "0.26.2", features = ["derive"] } thiserror = "1.0.58" time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] } diff --git a/crates/api_models/Cargo.toml b/crates/api_models/Cargo.toml index 0595bdc3e75d..9c9e1b850154 100644 --- a/crates/api_models/Cargo.toml +++ b/crates/api_models/Cargo.toml @@ -30,8 +30,7 @@ strum = { version = "0.26", features = ["derive"] } time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] } url = { version = "2.5.0", features = ["serde"] } utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order"] } -frunk = "0.4.2" -frunk_core = "0.4.2" + # First party crates cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } diff --git a/crates/api_models/src/enums.rs b/crates/api_models/src/enums.rs index 1161602435b0..2fc709bf1f42 100644 --- a/crates/api_models/src/enums.rs +++ b/crates/api_models/src/enums.rs @@ -618,7 +618,6 @@ pub enum LockerChoice { serde::Deserialize, strum::Display, strum::EnumString, - frunk::LabelledGeneric, ToSchema, )] #[serde(rename_all = "snake_case")] diff --git a/crates/common_enums/Cargo.toml b/crates/common_enums/Cargo.toml index c69676c01da0..e29261192fee 100644 --- a/crates/common_enums/Cargo.toml +++ b/crates/common_enums/Cargo.toml @@ -13,14 +13,12 @@ openapi = [] payouts = [] [dependencies] -thiserror = "1.0.58" diesel = { version = "2.1.5", features = ["postgres"] } serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" strum = { version = "0.26", features = ["derive"] } +thiserror = "1.0.58" utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order"] } -frunk = "0.4.2" -frunk_core = "0.4.2" # First party crates router_derive = { version = "0.1.0", path = "../router_derive" } diff --git a/crates/common_enums/src/enums.rs b/crates/common_enums/src/enums.rs index 5be7dcd66f97..4cc57d180a8e 100644 --- a/crates/common_enums/src/enums.rs +++ b/crates/common_enums/src/enums.rs @@ -326,7 +326,6 @@ pub enum AuthenticationType { serde::Deserialize, strum::Display, strum::EnumString, - frunk::LabelledGeneric, )] #[router_derive::diesel_enum(storage_type = "db_enum")] #[strum(serialize_all = "snake_case")] diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index f6c3d8b6d0dc..df6b44006336 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -17,6 +17,7 @@ metrics = ["dep:router_env", "dep:futures"] [dependencies] async-trait = { version = "0.1.79", optional = true } +blake3 = { version = "1.5.1", features = ["serde"] } bytes = "1.6.0" diesel = "2.1.5" error-stack = "0.4.1" @@ -25,6 +26,7 @@ hex = "0.4.3" http = "0.2.12" md5 = "0.7.0" nanoid = "0.4.0" +nutype = { version = "0.4.2", features = ["serde"] } once_cell = "1.19.0" phonenumber = "0.3.3" quick-xml = { version = "0.31.0", features = ["serialize"] } @@ -46,16 +48,11 @@ tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"], optional url = { version = "2.5.0", features = ["serde"] } utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order"] } uuid = { version = "1.8.0", features = ["v7"] } -blake3 = { version = "1.5.1", features = ["serde"] } - # First party crates -rusty-money = { git = "https://github.com/varunsrin/rusty_money", rev = "bbc0150742a0fff905225ff11ee09388e9babdcc", features = ["iso", "crypto"] } common_enums = { version = "0.1.0", path = "../common_enums" } masking = { version = "0.1.0", path = "../masking" } -router_derive = { version = "0.1.0", path = "../router_derive" } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"], optional = true } -nutype = { version = "0.4.2", features = ["serde"] } [target.'cfg(not(target_os = "windows"))'.dependencies] signal-hook-tokio = { version = "0.3.1", features = ["futures-v0_3"], optional = true } diff --git a/crates/connector_configs/Cargo.toml b/crates/connector_configs/Cargo.toml index de0c963d7b6e..4b0f810df29f 100644 --- a/crates/connector_configs/Cargo.toml +++ b/crates/connector_configs/Cargo.toml @@ -15,7 +15,10 @@ dummy_connector = ["api_models/dummy_connector", "development"] payouts = ["api_models/payouts"] [dependencies] +# First party crates api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } + +# Third party crates serde = { version = "1.0.197", features = ["derive"] } serde_with = "3.7.0" toml = "0.8.12" diff --git a/crates/currency_conversion/Cargo.toml b/crates/currency_conversion/Cargo.toml index 40129258a67d..bc255c57e166 100644 --- a/crates/currency_conversion/Cargo.toml +++ b/crates/currency_conversion/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] # First party crates common_enums = { version = "0.1.0", path = "../common_enums", package = "common_enums" } diff --git a/crates/diesel_models/Cargo.toml b/crates/diesel_models/Cargo.toml index 577233cdf1a3..f33427aaf309 100644 --- a/crates/diesel_models/Cargo.toml +++ b/crates/diesel_models/Cargo.toml @@ -15,8 +15,6 @@ kv_store = [] async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } diesel = { version = "2.1.5", features = ["postgres", "serde_json", "time", "64-column-tables"] } error-stack = "0.4.1" -frunk = "0.4.2" -frunk_core = "0.4.2" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" strum = { version = "0.26.2", features = ["derive"] } diff --git a/crates/diesel_models/src/enums.rs b/crates/diesel_models/src/enums.rs index 5eeed2990afc..96678a7a3e18 100644 --- a/crates/diesel_models/src/enums.rs +++ b/crates/diesel_models/src/enums.rs @@ -205,7 +205,6 @@ pub enum FraudCheckType { serde::Deserialize, strum::Display, strum::EnumString, - frunk::LabelledGeneric, )] #[diesel_enum(storage_type = "text")] #[strum(serialize_all = "snake_case")] @@ -228,7 +227,6 @@ pub enum FraudCheckLastStep { serde::Deserialize, strum::Display, strum::EnumString, - frunk::LabelledGeneric, )] #[diesel_enum(storage_type = "db_enum")] #[serde(rename_all = "snake_case")] @@ -249,7 +247,6 @@ pub enum UserStatus { serde::Serialize, strum::Display, strum::EnumString, - frunk::LabelledGeneric, )] #[router_derive::diesel_enum(storage_type = "db_enum")] #[serde(rename_all = "snake_case")] @@ -291,7 +288,6 @@ pub enum DashboardMetadata { serde::Deserialize, strum::Display, strum::EnumString, - frunk::LabelledGeneric, )] #[diesel_enum(storage_type = "db_enum")] #[serde(rename_all = "snake_case")] diff --git a/crates/drainer/Cargo.toml b/crates/drainer/Cargo.toml index 424ca6588473..c3e6b42135f1 100644 --- a/crates/drainer/Cargo.toml +++ b/crates/drainer/Cargo.toml @@ -14,6 +14,7 @@ vergen = ["router_env/vergen"] [dependencies] actix-web = "4.5.1" async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } +async-trait = "0.1.79" bb8 = "0.8" clap = { version = "4.4.18", default-features = false, features = ["std", "derive", "help", "usage"] } config = { version = "0.14.0", features = ["toml"] } @@ -27,7 +28,6 @@ serde_json = "1.0.115" serde_path_to_error = "0.1.16" thiserror = "1.0.58" tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] } -async-trait = "0.1.79" # First Party Crates common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals"] } diff --git a/crates/euclid/Cargo.toml b/crates/euclid/Cargo.toml index 63c9c1508d93..c8cf00eb416e 100644 --- a/crates/euclid/Cargo.toml +++ b/crates/euclid/Cargo.toml @@ -7,9 +7,6 @@ rust-version.workspace = true license.workspace = true [dependencies] -erased-serde = "0.4.4" -frunk = "0.4.2" -frunk_core = "0.4.2" nom = { version = "7.1.3", features = ["alloc"], optional = true } once_cell = "1.19.0" rustc-hash = "1.1.0" @@ -21,9 +18,9 @@ utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order # First party dependencies common_enums = { version = "0.1.0", path = "../common_enums" } -hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph", features = ["viz"] } +common_utils = { version = "0.1.0", path = "../common_utils" } euclid_macros = { version = "0.1.0", path = "../euclid_macros" } -common_utils = { version = "0.1.0", path = "../common_utils"} +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph", features = ["viz"] } [features] default = [] diff --git a/crates/euclid_wasm/Cargo.toml b/crates/euclid_wasm/Cargo.toml index be8f61240d66..47127c8212e2 100644 --- a/crates/euclid_wasm/Cargo.toml +++ b/crates/euclid_wasm/Cargo.toml @@ -6,7 +6,6 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [lib] crate-type = ["cdylib"] @@ -21,12 +20,12 @@ payouts = ["api_models/payouts", "euclid/payouts"] [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } -hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } -currency_conversion = { version = "0.1.0", path = "../currency_conversion" } +common_enums = { version = "0.1.0", path = "../common_enums" } connector_configs = { version = "0.1.0", path = "../connector_configs" } +currency_conversion = { version = "0.1.0", path = "../currency_conversion" } euclid = { version = "0.1.0", path = "../euclid", features = [] } +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } kgraph_utils = { version = "0.1.0", path = "../kgraph_utils" } -common_enums = { version = "0.1.0", path = "../common_enums" } # Third party crates getrandom = { version = "0.2.12", features = ["js"] } diff --git a/crates/events/Cargo.toml b/crates/events/Cargo.toml index 228fba60a685..335a3941d567 100644 --- a/crates/events/Cargo.toml +++ b/crates/events/Cargo.toml @@ -7,9 +7,9 @@ rust-version.workspace = true license.workspace = true [dependencies] -# First Party crates +# First Party crates masking = { version = "0.1.0", path = "../masking" } -router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"]} +router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } # Third Party crates error-stack = "0.4.1" diff --git a/crates/external_services/Cargo.toml b/crates/external_services/Cargo.toml index 31e22d4a6405..85df79701a78 100644 --- a/crates/external_services/Cargo.toml +++ b/crates/external_services/Cargo.toml @@ -17,21 +17,21 @@ hashicorp-vault = ["dep:vaultrs"] async-trait = "0.1.79" aws-config = { version = "0.55.3", optional = true } aws-sdk-kms = { version = "0.28.0", optional = true } +aws-sdk-s3 = { version = "0.28.0", optional = true } aws-sdk-sesv2 = "0.28.0" aws-sdk-sts = "0.28.0" -aws-sdk-s3 = { version = "0.28.0", optional = true } aws-smithy-client = "0.55.3" base64 = "0.22.0" dyn-clone = "1.0.17" error-stack = "0.4.1" +hex = "0.4.3" +hyper = "0.14.28" +hyper-proxy = "0.9.1" once_cell = "1.19.0" serde = { version = "1.0.197", features = ["derive"] } thiserror = "1.0.58" tokio = "1.37.0" -hyper-proxy = "0.9.1" -hyper = "0.14.28" vaultrs = { version = "0.7.2", optional = true } -hex = "0.4.3" # First party crates common_utils = { version = "0.1.0", path = "../common_utils" } diff --git a/crates/hsdev/Cargo.toml b/crates/hsdev/Cargo.toml index 6600d41d9fca..3864996ecee1 100644 --- a/crates/hsdev/Cargo.toml +++ b/crates/hsdev/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "hsdev" version = "0.1.0" -license.workspace = true +license.workspace = true edition.workspace = true -rust-version.workspace = true +rust-version.workspace = true description = "A simple diesel postgres migrator that uses TOML files" repository = "https://github.com/juspay/hyperswitch.git" readme = "README.md" [dependencies] +clap = { version = "4.1.8", features = ["derive"] } diesel = { version = "2.1.6", features = ["postgres"] } diesel_migrations = "2.1.0" -toml = "0.5" -clap = { version = "4.1.8", features = ["derive"] } serde = { version = "1.0", features = ["derive"] } +toml = "0.5" diff --git a/crates/hyperswitch_constraint_graph/Cargo.toml b/crates/hyperswitch_constraint_graph/Cargo.toml index 4fe97b2b1ac8..34cf535d08ec 100644 --- a/crates/hyperswitch_constraint_graph/Cargo.toml +++ b/crates/hyperswitch_constraint_graph/Cargo.toml @@ -5,8 +5,6 @@ version = "0.1.0" edition.workspace = true rust-version.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [features] viz = ["dep:graphviz-rust"] @@ -15,6 +13,5 @@ erased-serde = "0.3.28" graphviz-rust = { version = "0.6.2", optional = true } rustc-hash = "1.1.0" serde = { version = "1.0.163", features = ["derive", "rc"] } -serde_json = "1.0.96" strum = { version = "0.25", features = ["derive"] } thiserror = "1.0.43" diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml index 320d44cf21ed..ab5694646842 100644 --- a/crates/hyperswitch_domain_models/Cargo.toml +++ b/crates/hyperswitch_domain_models/Cargo.toml @@ -16,11 +16,11 @@ frm = ["api_models/frm"] [dependencies] # First party deps api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } +cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext", "metrics"] } -masking = { version = "0.1.0", path = "../masking" } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } -cards = { version = "0.1.0", path = "../cards" } +masking = { version = "0.1.0", path = "../masking" } router_derive = { version = "0.1.0", path = "../router_derive" } router_env = { version = "0.1.0", path = "../router_env" } @@ -28,6 +28,7 @@ router_env = { version = "0.1.0", path = "../router_env" } actix-web = "4.5.1" async-trait = "0.1.79" error-stack = "0.4.1" +futures = "0.3.30" http = "0.2.12" mime = "0.3.17" serde = { version = "1.0.197", features = ["derive"] } @@ -37,4 +38,3 @@ thiserror = "1.0.58" time = { version = "0.3.35", features = ["serde", "serde-well-known", "std"] } url = { version = "2.5.0", features = ["serde"] } utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order", "time"] } -futures = "0.3.30" diff --git a/crates/hyperswitch_interfaces/Cargo.toml b/crates/hyperswitch_interfaces/Cargo.toml index 3c481caef1bb..db61a05436b0 100644 --- a/crates/hyperswitch_interfaces/Cargo.toml +++ b/crates/hyperswitch_interfaces/Cargo.toml @@ -34,4 +34,3 @@ hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_m masking = { version = "0.1.0", path = "../masking" } router_derive = { version = "0.1.0", path = "../router_derive" } router_env = { version = "0.1.0", path = "../router_env" } -storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false } \ No newline at end of file diff --git a/crates/kgraph_utils/Cargo.toml b/crates/kgraph_utils/Cargo.toml index 6172e75629f6..d4110ae618f4 100644 --- a/crates/kgraph_utils/Cargo.toml +++ b/crates/kgraph_utils/Cargo.toml @@ -13,16 +13,16 @@ dummy_connector = ["api_models/dummy_connector", "euclid/dummy_connector"] [dependencies] api_models = { version = "0.1.0", path = "../api_models", package = "api_models" } common_enums = { version = "0.1.0", path = "../common_enums" } -hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph", features = ["viz"] } +common_utils = { version = "0.1.0", path = "../common_utils" } euclid = { version = "0.1.0", path = "../euclid" } +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph", features = ["viz"] } masking = { version = "0.1.0", path = "../masking/" } -common_utils = {version = "0.1.0", path = "../common_utils"} # Third party crates serde = "1.0.197" serde_json = "1.0.115" -thiserror = "1.0.58" strum = { version = "0.26", features = ["derive"] } +thiserror = "1.0.58" [dev-dependencies] criterion = "0.5" diff --git a/crates/masking/Cargo.toml b/crates/masking/Cargo.toml index 5c73ed7ea328..c65fdaa96a86 100644 --- a/crates/masking/Cargo.toml +++ b/crates/masking/Cargo.toml @@ -24,7 +24,7 @@ erased-serde = "0.4.4" serde = { version = "1", features = ["derive"], optional = true } serde_json = { version = "1.0.115", optional = true } subtle = "2.5.0" -time = {version = "0.3.35", optional = true, features = ["serde-human-readable"] } +time = { version = "0.3.35", optional = true, features = ["serde-human-readable"] } url = { version = "2.5.0", features = ["serde"] } zeroize = { version = "1.7", default-features = false } diff --git a/crates/openapi/Cargo.toml b/crates/openapi/Cargo.toml index 9b81225bcba2..e3e3bcc4a8fa 100644 --- a/crates/openapi/Cargo.toml +++ b/crates/openapi/Cargo.toml @@ -5,12 +5,12 @@ edition.workspace = true rust-version.workspace = true license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +# Third party crates serde_json = "1.0.115" utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order", "time"] } +# First party crates api_models = { version = "0.1.0", path = "../api_models", features = ["frm", "payouts", "openapi"] } common_utils = { version = "0.1.0", path = "../common_utils" } router_env = { version = "0.1.0", path = "../router_env" } diff --git a/crates/pm_auth/Cargo.toml b/crates/pm_auth/Cargo.toml index bb56db6f232b..a3c47e97d784 100644 --- a/crates/pm_auth/Cargo.toml +++ b/crates/pm_auth/Cargo.toml @@ -13,8 +13,6 @@ api_models = { version = "0.1.0", path = "../api_models" } common_enums = { version = "0.1.0", path = "../common_enums" } common_utils = { version = "0.1.0", path = "../common_utils" } masking = { version = "0.1.0", path = "../masking" } -router_derive = { version = "0.1.0", path = "../router_derive" } -router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } # Third party crates async-trait = "0.1.79" diff --git a/crates/redis_interface/Cargo.toml b/crates/redis_interface/Cargo.toml index d55ff86bceaf..e9dfc8cb5184 100644 --- a/crates/redis_interface/Cargo.toml +++ b/crates/redis_interface/Cargo.toml @@ -14,7 +14,7 @@ futures = "0.3" serde = { version = "1.0.197", features = ["derive"] } thiserror = "1.0.58" tokio = "1.37.0" -tokio-stream = {version = "0.1.15", features = ["sync"]} +tokio-stream = { version = "0.1.15", features = ["sync"] } tracing = { workspace = true } # First party crates diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 438e0dc323d9..1379b17eec44 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -11,12 +11,12 @@ license.workspace = true [features] default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls"] tls = ["actix-web/rustls-0_22"] -keymanager_mtls = ["reqwest/rustls-tls","common_utils/keymanager_mtls"] +keymanager_mtls = ["reqwest/rustls-tls", "common_utils/keymanager_mtls"] email = ["external_services/email", "scheduler/email", "olap"] keymanager_create = [] frm = ["api_models/frm", "hyperswitch_domain_models/frm"] stripe = ["dep:serde_qs"] -release = ["stripe", "email", "accounts_cache", "kv_store", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3","keymanager_mtls","keymanager_create"] +release = ["stripe", "email", "accounts_cache", "kv_store", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3", "keymanager_mtls", "keymanager_create"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] oltp = ["storage_impl/oltp"] kv_store = ["scheduler/kv_store"] @@ -33,11 +33,12 @@ v2 = ["api_models/v2"] [dependencies] actix-cors = "0.6.5" +actix-http = "3.6.0" actix-multipart = "0.6.1" actix-rt = "2.9.0" actix-web = "4.5.1" -async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } argon2 = { version = "0.5.3", features = ["std"] } +async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } async-trait = "0.1.79" base64 = "0.22.0" bb8 = "0.8" @@ -60,6 +61,8 @@ http = "0.2.12" hyper = "0.14.28" image = { version = "0.25.1", default-features = false, features = ["png"] } infer = "0.15.0" +iso_currency = "0.4.4" +isocountry = "0.3.2" josekit = "0.8.6" jsonwebtoken = "9.2.0" maud = { version = "0.26.0", features = ["actix-web"] } @@ -68,11 +71,13 @@ mime = "0.3.17" nanoid = "0.4.0" num_cpus = "1.16.0" once_cell = "1.19.0" -openidconnect = "3.5.0" # TODO: remove reqwest +openidconnect = "3.5.0" # TODO: remove reqwest openssl = "0.10.64" qrcode = "0.14.0" +quick-xml = { version = "0.31.0", features = ["serialize"] } rand = "0.8.5" rand_chacha = "0.3.1" +rdkafka = "0.36.2" regex = "1.10.4" reqwest = { version = "0.11.27", features = ["json", "native-tls", "__rustls", "gzip", "multipart"] } ring = "0.17.8" @@ -85,54 +90,47 @@ serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" serde_path_to_error = "0.1.16" serde_qs = { version = "0.12.0", optional = true } +serde_repr = "0.1.19" serde_urlencoded = "0.7.1" serde_with = "3.7.0" sha1 = { version = "0.10.6" } -sqlx = { version = "0.7.3", features = ["postgres", "runtime-tokio", "runtime-tokio-native-tls", "time", "bigdecimal"] } strum = { version = "0.26", features = ["derive"] } tera = "1.19.1" thiserror = "1.0.58" time = { version = "0.3.35", features = ["serde", "serde-well-known", "std", "parsing", "serde-human-readable"] } tokio = { version = "1.37.0", features = ["macros", "rt-multi-thread"] } +totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"] } +tracing-futures = { version = "0.2.5", features = ["tokio"] } unicode-segmentation = "1.11.0" +unidecode = "0.3.0" url = { version = "2.5.0", features = ["serde"] } utoipa = { version = "4.2.0", features = ["preserve_order", "preserve_path_order", "time"] } uuid = { version = "1.8.0", features = ["v4"] } validator = "0.17.0" x509-parser = "0.16.0" -tracing-futures = { version = "0.2.5", features = ["tokio"] } # First party crates -api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } analytics = { version = "0.1.0", path = "../analytics", optional = true } +api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } -common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics","keymanager"] } -hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } +common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics", "keymanager"] } currency_conversion = { version = "0.1.0", path = "../currency_conversion" } -hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } euclid = { version = "0.1.0", path = "../euclid", features = ["valued_jit"] } -pm_auth = { version = "0.1.0", path = "../pm_auth", package = "pm_auth" } +events = { version = "0.1.0", path = "../events" } external_services = { version = "0.1.0", path = "../external_services" } +hyperswitch_constraint_graph = { version = "0.1.0", path = "../hyperswitch_constraint_graph" } +hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } hyperswitch_interfaces = { version = "0.1.0", path = "../hyperswitch_interfaces" } kgraph_utils = { version = "0.1.0", path = "../kgraph_utils" } masking = { version = "0.1.0", path = "../masking" } +pm_auth = { version = "0.1.0", path = "../pm_auth", package = "pm_auth" } redis_interface = { version = "0.1.0", path = "../redis_interface" } router_derive = { version = "0.1.0", path = "../router_derive" } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } scheduler = { version = "0.1.0", path = "../scheduler", default-features = false } storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false } -erased-serde = "0.4.4" -quick-xml = { version = "0.31.0", features = ["serialize"] } -rdkafka = "0.36.2" -isocountry = "0.3.2" -iso_currency = "0.4.4" -actix-http = "3.6.0" -events = { version = "0.1.0", path = "../events" } -totp-rs = { version = "5.5.1", features = ["gen_secret", "otpauth"] } -serde_repr = "0.1.19" -unidecode = "0.3.0" [build-dependencies] router_env = { version = "0.1.0", path = "../router_env", default-features = false } diff --git a/crates/router_derive/Cargo.toml b/crates/router_derive/Cargo.toml index 99e50bf5528c..82535e1bd1d1 100644 --- a/crates/router_derive/Cargo.toml +++ b/crates/router_derive/Cargo.toml @@ -15,8 +15,8 @@ doctest = false indexmap = "2.2.6" proc-macro2 = "1.0.79" quote = "1.0.35" -syn = { version = "2.0.57", features = ["full", "extra-traits"] } # the full feature does not seem to encompass all the features strum = { version = "0.26.2", features = ["derive"] } +syn = { version = "2.0.57", features = ["full", "extra-traits"] } # the full feature does not seem to encompass all the features [dev-dependencies] diesel = { version = "2.1.5", features = ["postgres"] } diff --git a/crates/router_env/Cargo.toml b/crates/router_env/Cargo.toml index 59bfb4d5c8e3..b9b1184fedf9 100644 --- a/crates/router_env/Cargo.toml +++ b/crates/router_env/Cargo.toml @@ -14,7 +14,6 @@ error-stack = "0.4.1" gethostname = "0.4.3" once_cell = "1.19.0" opentelemetry = { version = "0.19.0", features = ["rt-tokio-current-thread", "metrics"] } -common_enums = { path = "../common_enums" } opentelemetry-otlp = { version = "0.12.0", features = ["metrics"] } rustc-hash = "1.1" serde = { version = "1.0.197", features = ["derive"] } diff --git a/crates/scheduler/Cargo.toml b/crates/scheduler/Cargo.toml index 73832a1ad6a9..14a0c8884e1f 100644 --- a/crates/scheduler/Cargo.toml +++ b/crates/scheduler/Cargo.toml @@ -31,12 +31,7 @@ uuid = { version = "1.8.0", features = ["v4"] } common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext"] } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } external_services = { version = "0.1.0", path = "../external_services" } -masking = { version = "0.1.0", path = "../masking" } +hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } redis_interface = { version = "0.1.0", path = "../redis_interface" } router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] } storage_impl = { version = "0.1.0", path = "../storage_impl", default-features = false } -hyperswitch_domain_models = {version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } - -# [[bin]] -# name = "scheduler" -# path = "src/bin/scheduler.rs" diff --git a/crates/storage_impl/Cargo.toml b/crates/storage_impl/Cargo.toml index d96d5ac7d571..54374643f444 100644 --- a/crates/storage_impl/Cargo.toml +++ b/crates/storage_impl/Cargo.toml @@ -7,7 +7,6 @@ rust-version.workspace = true readme = "README.md" license.workspace = true -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] default = ["olap", "oltp"] oltp = [] @@ -17,17 +16,16 @@ payouts = ["hyperswitch_domain_models/payouts"] [dependencies] # First Party dependencies api_models = { version = "0.1.0", path = "../api_models" } -common_utils = { version = "0.1.0", path = "../common_utils" } common_enums = { version = "0.1.0", path = "../common_enums" } -hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } +common_utils = { version = "0.1.0", path = "../common_utils" } diesel_models = { version = "0.1.0", path = "../diesel_models" } +hyperswitch_domain_models = { version = "0.1.0", path = "../hyperswitch_domain_models", default-features = false } masking = { version = "0.1.0", path = "../masking" } redis_interface = { version = "0.1.0", path = "../redis_interface" } router_derive = { version = "0.1.0", path = "../router_derive" } router_env = { version = "0.1.0", path = "../router_env" } # Third party crates -actix-web = "4.5.1" async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } async-trait = "0.1.79" bb8 = "0.8.3" @@ -38,8 +36,6 @@ diesel = { version = "2.1.5", default-features = false, features = ["postgres"] dyn-clone = "1.0.17" error-stack = "0.4.1" futures = "0.3.30" -http = "0.2.12" -mime = "0.3.17" moka = { version = "0.12", features = ["future"] } once_cell = "1.19.0" serde = { version = "1.0.197", features = ["derive"] } diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml index 6d8663c0300d..3b9eaa732925 100644 --- a/crates/test_utils/Cargo.toml +++ b/crates/test_utils/Cargo.toml @@ -13,8 +13,8 @@ dummy_connector = [] payouts = [] [dependencies] -async-trait = "0.1.79" anyhow = "1.0.81" +async-trait = "0.1.79" base64 = "0.22.0" clap = { version = "4.4.18", default-features = false, features = ["std", "derive", "help", "usage"] } rand = "0.8.5" From 817d06c7faa14493674931ba51ab6c32769602d1 Mon Sep 17 00:00:00 2001 From: Sakil Mostak <73734619+Sakilmostak@users.noreply.github.com> Date: Thu, 18 Jul 2024 19:44:51 +0530 Subject: [PATCH 17/26] fix(core): [payouts] failure of payout retrieve when token is expired (#5362) --- crates/router/src/core/payouts.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index 4365519aecc4..b2c883c381b2 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -2373,8 +2373,7 @@ pub async fn make_payout_data( validate_and_get_business_profile(state, &profile_id, merchant_id).await?; let payout_method_data = match req { payouts::PayoutRequest::PayoutCreateRequest(r) => r.payout_method_data.to_owned(), - payouts::PayoutRequest::PayoutRetrieveRequest(_) - | payouts::PayoutRequest::PayoutActionRequest(_) => { + payouts::PayoutRequest::PayoutActionRequest(_) => { match payout_attempt.payout_token.to_owned() { Some(payout_token) => { let customer_id = customer_details @@ -2397,6 +2396,7 @@ pub async fn make_payout_data( None => None, } } + payouts::PayoutRequest::PayoutRetrieveRequest(_) => None, }; let payout_link = payouts From fe14336f78b15e948b10d09b197fb1d529939b5c Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Thu, 18 Jul 2024 20:50:14 +0530 Subject: [PATCH 18/26] Docs: Updating Error codes for documentation purposes (#5314) Co-authored-by: Gorakhnath Yadav Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- .../src/errors/api_error_response.rs | 651 +++++++++--------- 1 file changed, 333 insertions(+), 318 deletions(-) diff --git a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs index e660850a9839..e42fdb93bec0 100644 --- a/crates/hyperswitch_domain_models/src/errors/api_error_response.rs +++ b/crates/hyperswitch_domain_models/src/errors/api_error_response.rs @@ -19,88 +19,13 @@ pub enum ErrorType { LockTimeout, } +// CE Connector Error Errors originating from connector's end +// HE Hyperswitch Error Errors originating from Hyperswitch's end +// IR Invalid Request Error Error caused due to invalid fields and values in API request +// WE Webhook Error Errors related to Webhooks #[derive(Debug, Clone, router_derive::ApiError)] #[error(error_type_enum = ErrorType)] pub enum ApiErrorResponse { - #[error(error_type = ErrorType::ServerNotAvailable, code = "IR_00", message = "{message:?}")] - NotImplemented { message: NotImplementedMessage }, - #[error( - error_type = ErrorType::InvalidRequestError, code = "IR_01", - message = "API key not provided or invalid API key used" - )] - Unauthorized, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_02", message = "Unrecognized request URL")] - InvalidRequestUrl, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "The HTTP method is not applicable for this API")] - InvalidHttpMethod, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "Missing required param: {field_name}")] - MissingRequiredField { field_name: &'static str }, - #[error( - error_type = ErrorType::InvalidRequestError, code = "IR_05", - message = "{field_name} contains invalid data. Expected format is {expected_format}" - )] - InvalidDataFormat { - field_name: String, - expected_format: String, - }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_06", message = "{message}")] - InvalidRequestData { message: String }, - /// Typically used when a field has invalid value, or deserialization of the value contained in a field fails. - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Invalid value provided: {field_name}")] - InvalidDataValue { field_name: &'static str }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Client secret was not provided")] - ClientSecretNotGiven, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Client secret has expired")] - ClientSecretExpired, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_09", message = "The client_secret provided does not match the client_secret associated with the Payment")] - ClientSecretInvalid, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_10", message = "Customer has active mandate/subsciption")] - MandateActive, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_11", message = "Customer has already been redacted")] - CustomerRedacted, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_12", message = "Reached maximum refund attempts")] - MaximumRefundCount, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_13", message = "The refund amount exceeds the amount captured")] - RefundAmountExceedsPaymentAmount, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_14", message = "This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}")] - PaymentUnexpectedState { - current_flow: String, - field_name: String, - current_value: String, - states: String, - }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_15", message = "Invalid Ephemeral Key for the customer")] - InvalidEphemeralKey, - /// Typically used when information involving multiple fields or previously provided information doesn't satisfy a condition. - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_16", message = "{message}")] - PreconditionFailed { message: String }, - #[error( - error_type = ErrorType::InvalidRequestError, code = "IR_17", - message = "Access forbidden, invalid JWT token was used" - )] - InvalidJwtToken, - #[error( - error_type = ErrorType::InvalidRequestError, code = "IR_18", - message = "{message}", - )] - GenericUnauthorized { message: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_19", message = "{message}")] - NotSupported { message: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_20", message = "{flow} flow not supported by the {connector} connector")] - FlowNotSupported { flow: String, connector: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_21", message = "Missing required params")] - MissingRequiredFields { field_names: Vec<&'static str> }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_22", message = "Access forbidden. Not authorized to access this resource {resource}")] - AccessForbidden { resource: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_23", message = "{message}")] - FileProviderNotSupported { message: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_23", message = "{message}")] - UnprocessableEntity { message: String }, - #[error( - error_type = ErrorType::ProcessingError, code = "IR_24", - message = "Invalid {wallet_name} wallet token" - )] - InvalidWalletToken { wallet_name: String }, #[error(error_type = ErrorType::ConnectorError, code = "CE_00", message = "{code}: {message}", ignore = "status_code")] ExternalConnectorError { code: String, @@ -117,8 +42,6 @@ pub enum ApiErrorResponse { PaymentCaptureFailed { data: Option }, #[error(error_type = ErrorType::ProcessingError, code = "CE_04", message = "The card data is invalid")] InvalidCardData { data: Option }, - #[error(error_type = ErrorType::InvalidRequestError, code = "CE_04", message = "Payout validation failed")] - PayoutFailed { data: Option }, #[error(error_type = ErrorType::ProcessingError, code = "CE_05", message = "The card has expired")] CardExpired { data: Option }, #[error(error_type = ErrorType::ProcessingError, code = "CE_06", message = "Refund failed while processing with connector. Retry refund")] @@ -127,10 +50,18 @@ pub enum ApiErrorResponse { VerificationFailed { data: Option }, #[error(error_type = ErrorType::ProcessingError, code = "CE_08", message = "Dispute operation failed while processing with connector. Retry operation")] DisputeFailed { data: Option }, - #[error(error_type = ErrorType::ServerNotAvailable, code = "HE_00", message = "Something went wrong")] - InternalServerError, + #[error(error_type = ErrorType::LockTimeout, code = "HE_00", message = "Resource is busy. Please try again later.")] ResourceBusy, + #[error(error_type = ErrorType::ServerNotAvailable, code = "HE_00", message = "Something went wrong")] + InternalServerError, + #[error(error_type = ErrorType::ServerNotAvailable, code= "HE_00", message = "{component} health check is failing with error: {message}")] + HealthCheckError { + component: &'static str, + message: String, + }, + #[error(error_type = ErrorType::ValidationError, code = "HE_00", message = "Failed to convert currency to minor unit")] + CurrencyConversionFailed, #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "Duplicate refund request. Refund already attempted with the refund ID")] DuplicateRefundRequest, #[error(error_type = ErrorType::DuplicateRequest, code = "HE_01", message = "Duplicate mandate request. Mandate already attempted with the Mandate ID")] @@ -152,9 +83,11 @@ pub enum ApiErrorResponse { DuplicateConfig, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Refund does not exist in our records")] RefundNotFound, + #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment Link does not exist in our records")] + PaymentLinkNotFound, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Customer does not exist in our records")] CustomerNotFound, - #[error(error_type = ErrorType::ObjectNotFound, code = "RE_02", message = "Config key does not exist in our records.")] + #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Config key does not exist in our records.")] ConfigNotFound, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment does not exist in our records")] PaymentNotFound, @@ -203,6 +136,10 @@ pub enum ApiErrorResponse { status: String, reason: String, }, + #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "File validation failed")] + FileValidationFailed { reason: String }, + #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "Dispute status validation failed")] + DisputeStatusValidationFailed { reason: String }, #[error(error_type= ErrorType::ObjectNotFound, code = "HE_04", message = "Successful payment not found for the given payment id")] SuccessfulPaymentNotFound, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "The connector provided in the request is incorrect or not available")] @@ -215,76 +152,145 @@ pub enum ApiErrorResponse { FileNotFound, #[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "File not available")] FileNotAvailable, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "Dispute status validation failed")] - DisputeStatusValidationFailed { reason: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "Card with the provided iin does not exist")] + #[error(error_type = ErrorType::ProcessingError, code = "HE_05", message = "Missing tenant id")] + MissingTenantId, + #[error(error_type = ErrorType::ProcessingError, code = "HE_05", message = "Invalid tenant id: {tenant_id}")] + InvalidTenant { tenant_id: String }, + #[error(error_type = ErrorType::ValidationError, code = "HE_06", message = "Failed to convert amount to {amount_type} type")] + AmountConversionFailed { amount_type: &'static str }, + #[error(error_type = ErrorType::ServerNotAvailable, code = "IR_00", message = "{message:?}")] + NotImplemented { message: NotImplementedMessage }, + #[error( + error_type = ErrorType::InvalidRequestError, code = "IR_01", + message = "API key not provided or invalid API key used" + )] + Unauthorized, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_02", message = "Unrecognized request URL")] + InvalidRequestUrl, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "The HTTP method is not applicable for this API")] + InvalidHttpMethod, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "Missing required param: {field_name}")] + MissingRequiredField { field_name: &'static str }, + #[error( + error_type = ErrorType::InvalidRequestError, code = "IR_05", + message = "{field_name} contains invalid data. Expected format is {expected_format}" + )] + InvalidDataFormat { + field_name: String, + expected_format: String, + }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_06", message = "{message}")] + InvalidRequestData { message: String }, + /// Typically used when a field has invalid value, or deserialization of the value contained in a field fails. + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_07", message = "Invalid value provided: {field_name}")] + InvalidDataValue { field_name: &'static str }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Client secret was not provided")] + ClientSecretNotGiven, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_08", message = "Client secret has expired")] + ClientSecretExpired, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_09", message = "The client_secret provided does not match the client_secret associated with the Payment")] + ClientSecretInvalid, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_10", message = "Customer has active mandate/subsciption")] + MandateActive, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_11", message = "Customer has already been redacted")] + CustomerRedacted, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_12", message = "Reached maximum refund attempts")] + MaximumRefundCount, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_13", message = "The refund amount exceeds the amount captured")] + RefundAmountExceedsPaymentAmount, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_14", message = "This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}")] + PaymentUnexpectedState { + current_flow: String, + field_name: String, + current_value: String, + states: String, + }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_15", message = "Invalid Ephemeral Key for the customer")] + InvalidEphemeralKey, + /// Typically used when information involving multiple fields or previously provided information doesn't satisfy a condition. + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_16", message = "{message}")] + PreconditionFailed { message: String }, + #[error( + error_type = ErrorType::InvalidRequestError, code = "IR_17", + message = "Access forbidden, invalid JWT token was used" + )] + InvalidJwtToken, + #[error( + error_type = ErrorType::InvalidRequestError, code = "IR_18", + message = "{message}", + )] + GenericUnauthorized { message: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_19", message = "{message}")] + NotSupported { message: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_20", message = "{flow} flow not supported by the {connector} connector")] + FlowNotSupported { flow: String, connector: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_21", message = "Missing required params")] + MissingRequiredFields { field_names: Vec<&'static str> }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_22", message = "Access forbidden. Not authorized to access this resource {resource}")] + AccessForbidden { resource: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_23", message = "{message}")] + FileProviderNotSupported { message: String }, + #[error( + error_type = ErrorType::ProcessingError, code = "IR_24", + message = "Invalid {wallet_name} wallet token" + )] + InvalidWalletToken { wallet_name: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_25", message = "Cannot delete the default payment method")] + PaymentMethodDeleteFailed, + #[error( + error_type = ErrorType::InvalidRequestError, code = "IR_26", + message = "Invalid Cookie" + )] + InvalidCookie, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_27", message = "Extended card info does not exist")] + ExtendedCardInfoNotFound, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_28", message = "{message}")] + CurrencyNotSupported { message: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_29", message = "{message}")] + UnprocessableEntity { message: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_30", message = "Merchant connector account is configured with invalid {config}")] + InvalidConnectorConfiguration { config: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_31", message = "Card with the provided iin does not exist")] InvalidCardIin, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "The provided card IIN length is invalid, please provide an iin with 6 or 8 digits")] + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_32", message = "The provided card IIN length is invalid, please provide an iin with 6 or 8 digits")] InvalidCardIinLength, - #[error(error_type = ErrorType::ValidationError, code = "HE_03", message = "File validation failed")] - FileValidationFailed { reason: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "File not found / valid in the request")] + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_33", message = "File not found / valid in the request")] MissingFile, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "Dispute id not found in the request")] + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_34", message = "Dispute id not found in the request")] MissingDisputeId, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "File purpose not found in the request or is invalid")] + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_35", message = "File purpose not found in the request or is invalid")] MissingFilePurpose, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "File content type not found / valid")] + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_36", message = "File content type not found / valid")] MissingFileContentType, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_05", message = "{message}")] + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_37", message = "{message}")] GenericNotFoundError { message: String }, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_01", message = "{message}")] + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_38", message = "{message}")] GenericDuplicateError { message: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_39", message = "required payment method is not configured or configured incorrectly for all configured connectors")] + IncorrectPaymentMethodConfiguration, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_40", message = "{message}")] + LinkConfigurationError { message: String }, + #[error(error_type = ErrorType::InvalidRequestError, code = "IR_41", message = "Payout validation failed")] + PayoutFailed { data: Option }, + #[error(error_type = ErrorType::InvalidRequestError, code = "WE_01", message = "Failed to authenticate the webhook")] WebhookAuthenticationFailed, - #[error(error_type = ErrorType::ObjectNotFound, code = "WE_04", message = "Webhook resource not found")] - WebhookResourceNotFound, #[error(error_type = ErrorType::InvalidRequestError, code = "WE_02", message = "Bad request received in webhook")] WebhookBadRequest, #[error(error_type = ErrorType::RouterError, code = "WE_03", message = "There was some issue processing the webhook")] WebhookProcessingFailure, - #[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", message = "required payment method is not configured or configured incorrectly for all configured connectors")] - IncorrectPaymentMethodConfiguration, + #[error(error_type = ErrorType::ObjectNotFound, code = "WE_04", message = "Webhook resource not found")] + WebhookResourceNotFound, #[error(error_type = ErrorType::InvalidRequestError, code = "WE_05", message = "Unable to process the webhook body")] WebhookUnprocessableEntity, - #[error(error_type = ErrorType::ObjectNotFound, code = "HE_02", message = "Payment Link does not exist in our records")] - PaymentLinkNotFound, - #[error(error_type = ErrorType::InvalidRequestError, code = "WE_05", message = "Merchant Secret set my merchant for webhook source verification is invalid")] + #[error(error_type = ErrorType::InvalidRequestError, code = "WE_06", message = "Merchant Secret set my merchant for webhook source verification is invalid")] WebhookInvalidMerchantSecret, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_19", message = "{message}")] - CurrencyNotSupported { message: String }, - #[error(error_type = ErrorType::ServerNotAvailable, code= "HE_00", message = "{component} health check is failing with error: {message}")] - HealthCheckError { - component: &'static str, - message: String, - }, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_24", message = "Merchant connector account is configured with invalid {config}")] - InvalidConnectorConfiguration { config: String }, - #[error(error_type = ErrorType::ValidationError, code = "HE_01", message = "Failed to convert currency to minor unit")] - CurrencyConversionFailed, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_25", message = "Cannot delete the default payment method")] - PaymentMethodDeleteFailed, - #[error( - error_type = ErrorType::InvalidRequestError, code = "IR_26", - message = "Invalid Cookie" - )] - InvalidCookie, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_27", message = "Extended card info does not exist")] - ExtendedCardInfoNotFound, - #[error(error_type = ErrorType::InvalidRequestError, code = "IR_28", message = "{message}")] - LinkConfigurationError { message: String }, - #[error(error_type = ErrorType::ServerNotAvailable, code = "IE", message = "{reason} as data mismatched for {field_names}", ignore = "status_code")] + #[error(error_type = ErrorType::ServerNotAvailable, code = "IE", message = "{reason} as data mismatched for {field_names}")] IntegrityCheckFailed { reason: String, field_names: String, connector_transaction_id: Option, }, - #[error(error_type = ErrorType::ProcessingError, code = "HE_06", message = "Missing tenant id")] - MissingTenantId, - #[error(error_type = ErrorType::ProcessingError, code = "HE_06", message = "Invalid tenant id: {tenant_id}")] - InvalidTenant { tenant_id: String }, - #[error(error_type = ErrorType::ValidationError, code = "HE_01", message = "Failed to convert amount to {amount_type} type")] - AmountConversionFailed { amount_type: &'static str }, } #[derive(Clone)] @@ -322,132 +328,44 @@ impl ErrorSwitch for ApiErrorRespon use api_models::errors::types::{ApiError, ApiErrorResponse as AER}; match self { - Self::NotImplemented { message } => { - AER::NotImplemented(ApiError::new("IR", 0, format!("{message:?}"), None)) + Self::ExternalConnectorError { + code, + message, + connector, + reason, + status_code, + } => AER::ConnectorError(ApiError::new("CE", 0, format!("{code}: {message}"), Some(Extra {connector: Some(connector.clone()), reason: reason.to_owned().map(Into::into), ..Default::default()})), StatusCode::from_u16(*status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)), + Self::PaymentAuthorizationFailed { data } => { + AER::BadRequest(ApiError::new("CE", 1, "Payment failed during authorization with connector. Retry payment", Some(Extra { data: data.clone(), ..Default::default()}))) } - Self::Unauthorized => AER::Unauthorized(ApiError::new( - "IR", - 1, - "API key not provided or invalid API key used", None - )), - Self::InvalidRequestUrl => { - AER::NotFound(ApiError::new("IR", 2, "Unrecognized request URL", None)) - } - Self::InvalidHttpMethod => AER::MethodNotAllowed(ApiError::new( - "IR", - 3, - "The HTTP method is not applicable for this API", None - )), - Self::MissingRequiredField { field_name } => AER::BadRequest( - ApiError::new("IR", 4, format!("Missing required param: {field_name}"), None), - ), - Self::InvalidDataFormat { - field_name, - expected_format, - } => AER::Unprocessable(ApiError::new( - "IR", - 5, - format!( - "{field_name} contains invalid data. Expected format is {expected_format}" - ), None - )), - Self::InvalidRequestData { message } => { - AER::Unprocessable(ApiError::new("IR", 6, message.to_string(), None)) - } - Self::InvalidDataValue { field_name } => AER::BadRequest(ApiError::new( - "IR", - 7, - format!("Invalid value provided: {field_name}"), None - )), - Self::ClientSecretNotGiven => AER::BadRequest(ApiError::new( - "IR", - 8, - "client_secret was not provided", None - )), - Self::ClientSecretInvalid => { - AER::BadRequest(ApiError::new("IR", 9, "The client_secret provided does not match the client_secret associated with the Payment", None)) - } - Self::CurrencyNotSupported { message } => { - AER::BadRequest(ApiError::new("IR", 9, message, None)) - } - Self::MandateActive => { - AER::BadRequest(ApiError::new("IR", 10, "Customer has active mandate/subsciption", None)) - } - Self::CustomerRedacted => { - AER::BadRequest(ApiError::new("IR", 11, "Customer has already been redacted", None)) - } - Self::MaximumRefundCount => AER::BadRequest(ApiError::new("IR", 12, "Reached maximum refund attempts", None)), - Self::RefundAmountExceedsPaymentAmount => { - AER::BadRequest(ApiError::new("IR", 13, "The refund amount exceeds the amount captured", None)) - } - Self::PaymentUnexpectedState { - current_flow, - field_name, - current_value, - states, - } => AER::BadRequest(ApiError::new("IR", 14, format!("This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}"), None)), - Self::InvalidEphemeralKey => AER::Unauthorized(ApiError::new("IR", 15, "Invalid Ephemeral Key for the customer", None)), - Self::PreconditionFailed { message } => { - AER::BadRequest(ApiError::new("IR", 16, message.to_string(), None)) - } - Self::InvalidJwtToken => AER::Unauthorized(ApiError::new("IR", 17, "Access forbidden, invalid JWT token was used", None)), - Self::GenericUnauthorized { message } => { - AER::Unauthorized(ApiError::new("IR", 18, message.to_string(), None)) - }, - Self::ClientSecretExpired => AER::BadRequest(ApiError::new( - "IR", - 19, - "The provided client_secret has expired", None - )), - Self::MissingRequiredFields { field_names } => AER::BadRequest( - ApiError::new("IR", 21, "Missing required params".to_string(), Some(Extra {data: Some(serde_json::json!(field_names)), ..Default::default() })), - ), - Self::AccessForbidden {resource} => { - AER::ForbiddenCommonResource(ApiError::new("IR", 22, format!("Access forbidden. Not authorized to access this resource {resource}"), None)) - }, - Self::FileProviderNotSupported { message } => { - AER::BadRequest(ApiError::new("IR", 23, message.to_string(), None)) - }, - Self::UnprocessableEntity {message} => AER::Unprocessable(ApiError::new("IR", 23, message.to_string(), None)), - Self::InvalidWalletToken { wallet_name} => AER::Unprocessable(ApiError::new( - "IR", - 24, - format!("Invalid {wallet_name} wallet token"), None - )), - Self::ExternalConnectorError { - code, - message, - connector, - reason, - status_code, - } => AER::ConnectorError(ApiError::new("CE", 0, format!("{code}: {message}"), Some(Extra {connector: Some(connector.clone()), reason: reason.to_owned().map(Into::into), ..Default::default()})), StatusCode::from_u16(*status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)), - Self::PaymentAuthorizationFailed { data } => { - AER::BadRequest(ApiError::new("CE", 1, "Payment failed during authorization with connector. Retry payment", Some(Extra { data: data.clone(), ..Default::default()}))) - } - Self::PaymentAuthenticationFailed { data } => { - AER::BadRequest(ApiError::new("CE", 2, "Payment failed during authentication with connector. Retry payment", Some(Extra { data: data.clone(), ..Default::default()}))) + Self::PaymentAuthenticationFailed { data } => { + AER::BadRequest(ApiError::new("CE", 2, "Payment failed during authentication with connector. Retry payment", Some(Extra { data: data.clone(), ..Default::default()}))) } Self::PaymentCaptureFailed { data } => { AER::BadRequest(ApiError::new("CE", 3, "Capture attempt failed while processing with connector", Some(Extra { data: data.clone(), ..Default::default()}))) } - Self::DisputeFailed { data } => { - AER::BadRequest(ApiError::new("CE", 1, "Dispute operation failed while processing with connector. Retry operation", Some(Extra { data: data.clone(), ..Default::default()}))) - } Self::InvalidCardData { data } => AER::BadRequest(ApiError::new("CE", 4, "The card data is invalid", Some(Extra { data: data.clone(), ..Default::default()}))), Self::CardExpired { data } => AER::BadRequest(ApiError::new("CE", 5, "The card has expired", Some(Extra { data: data.clone(), ..Default::default()}))), Self::RefundFailed { data } => AER::BadRequest(ApiError::new("CE", 6, "Refund failed while processing with connector. Retry refund", Some(Extra { data: data.clone(), ..Default::default()}))), Self::VerificationFailed { data } => { AER::BadRequest(ApiError::new("CE", 7, "Verification failed while processing with connector. Retry operation", Some(Extra { data: data.clone(), ..Default::default()}))) }, - Self::MandateUpdateFailed | Self::MandateSerializationFailed | Self::MandateDeserializationFailed | Self::InternalServerError => { + Self::DisputeFailed { data } => { + AER::BadRequest(ApiError::new("CE", 8, "Dispute operation failed while processing with connector. Retry operation", Some(Extra { data: data.clone(), ..Default::default()}))) + } + + Self::ResourceBusy => { + AER::Unprocessable(ApiError::new("HE", 0, "There was an issue processing the webhook body", None)) + } + Self::CurrencyConversionFailed => { + AER::Unprocessable(ApiError::new("HE", 0, "Failed to convert currency to minor unit", None)) + } + Self::InternalServerError => { AER::InternalServerError(ApiError::new("HE", 0, "Something went wrong", None)) }, Self::HealthCheckError { message,component } => { AER::InternalServerError(ApiError::new("HE",0,format!("{} health check failed with error: {}",component,message),None)) }, - Self::PayoutFailed { data } => { - AER::BadRequest(ApiError::new("CE", 4, "Payout failed while processing with connector.", Some(Extra { data: data.clone(), ..Default::default()}))) - }, Self::DuplicateRefundRequest => AER::BadRequest(ApiError::new("HE", 1, "Duplicate refund request. Refund already attempted with the refund ID", None)), Self::DuplicateMandate => AER::BadRequest(ApiError::new("HE", 1, "Duplicate mandate request. Mandate already attempted with the Mandate ID", None)), Self::DuplicateMerchantAccount => AER::BadRequest(ApiError::new("HE", 1, "The merchant account with the specified details already exists in our records", None)), @@ -461,21 +379,21 @@ impl ErrorSwitch for ApiErrorRespon Self::DuplicatePayout { payout_id } => { AER::BadRequest(ApiError::new("HE", 1, format!("The payout with the specified payout_id '{payout_id}' already exists in our records"), None)) } - Self::GenericDuplicateError { message } => { - AER::BadRequest(ApiError::new("HE", 1, message, None)) + Self::DuplicateConfig => { + AER::BadRequest(ApiError::new("HE", 1, "The config with the specified key already exists in our records", None)) } Self::RefundNotFound => { AER::NotFound(ApiError::new("HE", 2, "Refund does not exist in our records.", None)) } + Self::PaymentLinkNotFound => { + AER::NotFound(ApiError::new("HE", 2, "Payment Link does not exist in our records", None)) + } Self::CustomerNotFound => { AER::NotFound(ApiError::new("HE", 2, "Customer does not exist in our records", None)) } Self::ConfigNotFound => { AER::NotFound(ApiError::new("HE", 2, "Config key does not exist in our records.", None)) }, - Self::DuplicateConfig => { - AER::BadRequest(ApiError::new("HE", 1, "The config with the specified key already exists in our records", None)) - } Self::PaymentNotFound => { AER::NotFound(ApiError::new("HE", 2, "Payment does not exist in our records", None)) } @@ -488,21 +406,36 @@ impl ErrorSwitch for ApiErrorRespon Self::MerchantConnectorAccountNotFound {id } => { AER::NotFound(ApiError::new("HE", 2, "Merchant connector account does not exist in our records", Some(Extra {reason: Some(format!("{id} does not exist")), ..Default::default()}))) } - Self::MerchantConnectorAccountDisabled => { - AER::BadRequest(ApiError::new("HE", 3, "The selected merchant connector account is disabled", None)) + Self::BusinessProfileNotFound { id } => { + AER::NotFound(ApiError::new("HE", 2, format!("Business profile with the given id {id} does not exist"), None)) } + Self::PollNotFound { .. } => { + AER::NotFound(ApiError::new("HE", 2, "Poll does not exist in our records", None)) + }, Self::ResourceIdNotFound => { AER::NotFound(ApiError::new("HE", 2, "Resource ID does not exist in our records", None)) } Self::MandateNotFound => { AER::NotFound(ApiError::new("HE", 2, "Mandate does not exist in our records", None)) } + Self::AuthenticationNotFound { .. } => { + AER::NotFound(ApiError::new("HE", 2, "Authentication does not exist in our records", None)) + }, + Self::MandateUpdateFailed => { + AER::InternalServerError(ApiError::new("HE", 2, "Mandate update failed", None)) + }, + Self::ApiKeyNotFound => { + AER::NotFound(ApiError::new("HE", 2, "API Key does not exist in our records", None)) + } Self::PayoutNotFound => { AER::NotFound(ApiError::new("HE", 2, "Payout does not exist in our records", None)) } Self::EventNotFound => { AER::NotFound(ApiError::new("HE", 2, "Event does not exist in our records", None)) } + Self::MandateSerializationFailed | Self::MandateDeserializationFailed => { + AER::InternalServerError(ApiError::new("HE", 3, "Something went wrong", None)) + }, Self::ReturnUrlUnavailable => AER::NotFound(ApiError::new("HE", 3, "Return URL is not configured and not passed in payments request", None)), Self::RefundNotPossible { connector } => { AER::BadRequest(ApiError::new("HE", 3, format!("This refund is not possible through Hyperswitch. Please raise the refund through {connector} dashboard"), None)) @@ -511,11 +444,20 @@ impl ErrorSwitch for ApiErrorRespon AER::BadRequest(ApiError::new("HE", 3, "Mandate Validation Failed", Some(Extra { reason: Some(reason.to_owned()), ..Default::default() }))) } Self::PaymentNotSucceeded => AER::BadRequest(ApiError::new("HE", 3, "The payment has not succeeded yet. Please pass a successful payment to initiate refund", None)), + Self::MerchantConnectorAccountDisabled => { + AER::BadRequest(ApiError::new("HE", 3, "The selected merchant connector account is disabled", None)) + } Self::PaymentBlockedError { message, reason, .. } => AER::DomainError(ApiError::new("HE", 3, message, Some(Extra { reason: Some(reason.clone()), ..Default::default() }))), + Self::FileValidationFailed { reason } => { + AER::BadRequest(ApiError::new("HE", 3, format!("File validation failed {reason}"), None)) + } + Self::DisputeStatusValidationFailed { .. } => { + AER::BadRequest(ApiError::new("HE", 3, "Dispute status validation failed", None)) + } Self::SuccessfulPaymentNotFound => { AER::NotFound(ApiError::new("HE", 4, "Successful payment not found for the given payment id", None)) } @@ -525,101 +467,183 @@ impl ErrorSwitch for ApiErrorRespon Self::AddressNotFound => { AER::NotFound(ApiError::new("HE", 4, "Address does not exist in our records", None)) }, - Self::GenericNotFoundError { message } => { - AER::NotFound(ApiError::new("HE", 5, message, None)) + Self::DisputeNotFound { .. } => { + AER::NotFound(ApiError::new("HE", 4, "Dispute does not exist in our records", None)) }, - Self::ApiKeyNotFound => { - AER::NotFound(ApiError::new("HE", 2, "API Key does not exist in our records", None)) + Self::FileNotFound => { + AER::NotFound(ApiError::new("HE", 4, "File does not exist in our records", None)) + } + Self::FileNotAvailable => { + AER::NotFound(ApiError::new("HE", 4, "File not available", None)) } + Self::MissingTenantId => { + AER::InternalServerError(ApiError::new("HE", 5, "Missing Tenant ID in the request".to_string(), None)) + } + Self::InvalidTenant { tenant_id } => { + AER::InternalServerError(ApiError::new("HE", 5, format!("Invalid Tenant {tenant_id}"), None)) + } + Self::AmountConversionFailed { amount_type } => { + AER::InternalServerError(ApiError::new("HE", 6, format!("Failed to convert amount to {amount_type} type"), None)) + } + + Self::NotImplemented { message } => { + AER::NotImplemented(ApiError::new("IR", 0, format!("{message:?}"), None)) + } + Self::Unauthorized => AER::Unauthorized(ApiError::new( + "IR", + 1, + "API key not provided or invalid API key used", None + )), + Self::InvalidRequestUrl => { + AER::NotFound(ApiError::new("IR", 2, "Unrecognized request URL", None)) + } + Self::InvalidHttpMethod => AER::MethodNotAllowed(ApiError::new( + "IR", + 3, + "The HTTP method is not applicable for this API", None + )), + Self::MissingRequiredField { field_name } => AER::BadRequest( + ApiError::new("IR", 4, format!("Missing required param: {field_name}"), None), + ), + Self::InvalidDataFormat { + field_name, + expected_format, + } => AER::Unprocessable(ApiError::new( + "IR", + 5, + format!( + "{field_name} contains invalid data. Expected format is {expected_format}" + ), None + )), + Self::InvalidRequestData { message } => { + AER::Unprocessable(ApiError::new("IR", 6, message.to_string(), None)) + } + Self::InvalidDataValue { field_name } => AER::BadRequest(ApiError::new( + "IR", + 7, + format!("Invalid value provided: {field_name}"), None + )), + Self::ClientSecretNotGiven => AER::BadRequest(ApiError::new( + "IR", + 8, + "client_secret was not provided", None + )), + Self::ClientSecretExpired => AER::BadRequest(ApiError::new( + "IR", + 8, + "The provided client_secret has expired", None + )), + Self::ClientSecretInvalid => { + AER::BadRequest(ApiError::new("IR", 9, "The client_secret provided does not match the client_secret associated with the Payment", None)) + } + Self::MandateActive => { + AER::BadRequest(ApiError::new("IR", 10, "Customer has active mandate/subsciption", None)) + } + Self::CustomerRedacted => { + AER::BadRequest(ApiError::new("IR", 11, "Customer has already been redacted", None)) + } + Self::MaximumRefundCount => AER::BadRequest(ApiError::new("IR", 12, "Reached maximum refund attempts", None)), + Self::RefundAmountExceedsPaymentAmount => { + AER::BadRequest(ApiError::new("IR", 13, "The refund amount exceeds the amount captured", None)) + } + Self::PaymentUnexpectedState { + current_flow, + field_name, + current_value, + states, + } => AER::BadRequest(ApiError::new("IR", 14, format!("This Payment could not be {current_flow} because it has a {field_name} of {current_value}. The expected state is {states}"), None)), + Self::InvalidEphemeralKey => AER::Unauthorized(ApiError::new("IR", 15, "Invalid Ephemeral Key for the customer", None)), + Self::PreconditionFailed { message } => { + AER::BadRequest(ApiError::new("IR", 16, message.to_string(), None)) + } + Self::InvalidJwtToken => AER::Unauthorized(ApiError::new("IR", 17, "Access forbidden, invalid JWT token was used", None)), + Self::GenericUnauthorized { message } => { + AER::Unauthorized(ApiError::new("IR", 18, message.to_string(), None)) + }, Self::NotSupported { message } => { - AER::BadRequest(ApiError::new("HE", 3, "Payment method type not supported", Some(Extra {reason: Some(message.to_owned()), ..Default::default()}))) + AER::BadRequest(ApiError::new("IR", 19, "Payment method type not supported", Some(Extra {reason: Some(message.to_owned()), ..Default::default()}))) }, - Self::InvalidCardIin => AER::BadRequest(ApiError::new("HE", 3, "The provided card IIN does not exist", None)), - Self::InvalidCardIinLength => AER::BadRequest(ApiError::new("HE", 3, "The provided card IIN length is invalid, please provide an IIN with 6 digits", None)), Self::FlowNotSupported { flow, connector } => { AER::BadRequest(ApiError::new("IR", 20, format!("{flow} flow not supported"), Some(Extra {connector: Some(connector.to_owned()), ..Default::default()}))) //FIXME: error message } - Self::DisputeNotFound { .. } => { - AER::NotFound(ApiError::new("HE", 2, "Dispute does not exist in our records", None)) + Self::MissingRequiredFields { field_names } => AER::BadRequest( + ApiError::new("IR", 21, "Missing required params".to_string(), Some(Extra {data: Some(serde_json::json!(field_names)), ..Default::default() })), + ), + Self::AccessForbidden {resource} => { + AER::ForbiddenCommonResource(ApiError::new("IR", 22, format!("Access forbidden. Not authorized to access this resource {resource}"), None)) }, - Self::AuthenticationNotFound { .. } => { - AER::NotFound(ApiError::new("HE", 2, "Authentication does not exist in our records", None)) + Self::FileProviderNotSupported { message } => { + AER::BadRequest(ApiError::new("IR", 23, message.to_string(), None)) }, - Self::BusinessProfileNotFound { id } => { - AER::NotFound(ApiError::new("HE", 2, format!("Business profile with the given id {id} does not exist"), None)) + Self::InvalidWalletToken { wallet_name} => AER::Unprocessable(ApiError::new( + "IR", + 24, + format!("Invalid {wallet_name} wallet token"), None + )), + Self::PaymentMethodDeleteFailed => { + AER::BadRequest(ApiError::new("IR", 25, "Cannot delete the default payment method", None)) } - Self::FileNotFound => { - AER::NotFound(ApiError::new("HE", 2, "File does not exist in our records", None)) + Self::InvalidCookie => { + AER::BadRequest(ApiError::new("IR", 26, "Invalid Cookie", None)) } - Self::PollNotFound { .. } => { - AER::NotFound(ApiError::new("HE", 2, "Poll does not exist in our records", None)) - }, - Self::FileNotAvailable => { - AER::NotFound(ApiError::new("HE", 2, "File not available", None)) + Self::ExtendedCardInfoNotFound => { + AER::NotFound(ApiError::new("IR", 27, "Extended card info does not exist", None)) } - Self::DisputeStatusValidationFailed { .. } => { - AER::BadRequest(ApiError::new("HE", 2, "Dispute status validation failed", None)) + Self::CurrencyNotSupported { message } => { + AER::BadRequest(ApiError::new("IR", 28, message, None)) } - Self::FileValidationFailed { reason } => { - AER::BadRequest(ApiError::new("HE", 2, format!("File validation failed {reason}"), None)) + Self::UnprocessableEntity {message} => AER::Unprocessable(ApiError::new("IR", 29, message.to_string(), None)), + Self::InvalidConnectorConfiguration {config} => { + AER::BadRequest(ApiError::new("IR", 30, format!("Merchant connector account is configured with invalid {config}"), None)) } + Self::InvalidCardIin => AER::BadRequest(ApiError::new("IR", 31, "The provided card IIN does not exist", None)), + Self::InvalidCardIinLength => AER::BadRequest(ApiError::new("IR", 32, "The provided card IIN length is invalid, please provide an IIN with 6 digits", None)), Self::MissingFile => { - AER::BadRequest(ApiError::new("HE", 2, "File not found in the request", None)) + AER::BadRequest(ApiError::new("IR", 33, "File not found in the request", None)) + } + Self::MissingDisputeId => { + AER::BadRequest(ApiError::new("IR", 34, "Dispute id not found in the request", None)) } Self::MissingFilePurpose => { - AER::BadRequest(ApiError::new("HE", 2, "File purpose not found in the request or is invalid", None)) + AER::BadRequest(ApiError::new("IR", 35, "File purpose not found in the request or is invalid", None)) } Self::MissingFileContentType => { - AER::BadRequest(ApiError::new("HE", 2, "File content type not found", None)) + AER::BadRequest(ApiError::new("IR", 36, "File content type not found", None)) } - Self::MissingDisputeId => { - AER::BadRequest(ApiError::new("HE", 2, "Dispute id not found in the request", None)) + Self::GenericNotFoundError { message } => { + AER::NotFound(ApiError::new("IR", 37, message, None)) + }, + Self::GenericDuplicateError { message } => { + AER::BadRequest(ApiError::new("IR", 38, message, None)) + } + Self::IncorrectPaymentMethodConfiguration => { + AER::BadRequest(ApiError::new("IR", 39, "No eligible connector was found for the current payment method configuration", None)) } + Self::LinkConfigurationError { message } => { + AER::BadRequest(ApiError::new("IR", 40, message, None)) + }, + Self::PayoutFailed { data } => { + AER::BadRequest(ApiError::new("IR", 41, "Payout failed while processing with connector.", Some(Extra { data: data.clone(), ..Default::default()}))) + }, + Self::WebhookAuthenticationFailed => { AER::Unauthorized(ApiError::new("WE", 1, "Webhook authentication failed", None)) } - Self::WebhookResourceNotFound => { - AER::NotFound(ApiError::new("WE", 4, "Webhook resource was not found", None)) - } Self::WebhookBadRequest => { AER::BadRequest(ApiError::new("WE", 2, "Bad request body received", None)) } Self::WebhookProcessingFailure => { AER::InternalServerError(ApiError::new("WE", 3, "There was an issue processing the webhook", None)) }, - Self::WebhookInvalidMerchantSecret => { - AER::BadRequest(ApiError::new("WE", 2, "Merchant Secret set for webhook source verificartion is invalid", None)) - } - Self::IncorrectPaymentMethodConfiguration => { - AER::BadRequest(ApiError::new("HE", 4, "No eligible connector was found for the current payment method configuration", None)) + Self::WebhookResourceNotFound => { + AER::NotFound(ApiError::new("WE", 4, "Webhook resource was not found", None)) } Self::WebhookUnprocessableEntity => { AER::Unprocessable(ApiError::new("WE", 5, "There was an issue processing the webhook body", None)) }, - Self::ResourceBusy => { - AER::Unprocessable(ApiError::new("WE", 5, "There was an issue processing the webhook body", None)) - } - Self::PaymentLinkNotFound => { - AER::NotFound(ApiError::new("HE", 2, "Payment Link does not exist in our records", None)) - } - Self::InvalidConnectorConfiguration {config} => { - AER::BadRequest(ApiError::new("IR", 24, format!("Merchant connector account is configured with invalid {config}"), None)) - } - Self::CurrencyConversionFailed => { - AER::Unprocessable(ApiError::new("HE", 2, "Failed to convert currency to minor unit", None)) - } - Self::PaymentMethodDeleteFailed => { - AER::BadRequest(ApiError::new("IR", 25, "Cannot delete the default payment method", None)) - } - Self::InvalidCookie => { - AER::BadRequest(ApiError::new("IR", 26, "Invalid Cookie", None)) - } - Self::ExtendedCardInfoNotFound => { - AER::NotFound(ApiError::new("IR", 27, "Extended card info does not exist", None)) + Self::WebhookInvalidMerchantSecret => { + AER::BadRequest(ApiError::new("WE", 6, "Merchant Secret set for webhook source verificartion is invalid", None)) } - Self::LinkConfigurationError { message } => { - AER::BadRequest(ApiError::new("IR", 28, message, None)) - }, Self::IntegrityCheckFailed { reason, field_names, @@ -633,15 +657,6 @@ impl ErrorSwitch for ApiErrorRespon ..Default::default() }) )), - Self::MissingTenantId => { - AER::InternalServerError(ApiError::new("HE", 6, "Missing Tenant ID in the request".to_string(), None)) - } - Self::InvalidTenant { tenant_id } => { - AER::InternalServerError(ApiError::new("HE", 6, format!("Invalid Tenant {tenant_id}"), None)) - } - Self::AmountConversionFailed { amount_type } => { - AER::InternalServerError(ApiError::new("HE", 6, format!("Failed to convert amount to {amount_type} type"), None)) - } } } } From 93dd60c9ab8180ed7688959e32a3c2730c2bfad2 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 05:00:51 +0000 Subject: [PATCH 19/26] chore(postman): update Postman collection files --- .../adyen_uk.postman_collection.json | 6 +- .../authorizedotnet.postman_collection.json | 4 +- .../bankofamerica.postman_collection.json | 12 +- .../bluesnap.postman_collection.json | 2 +- .../checkout.postman_collection.json | 10 +- .../cybersource.postman_collection.json | 2 +- .../forte.postman_collection.json | 4 +- .../hyperswitch.postman_collection.json | 2 +- .../multisafepay.postman_collection.json | 4 +- .../nmi.postman_collection.json | 6 +- .../paypal.postman_collection.json | 2 +- .../prophetpay.postman_collection.json | 6 +- .../stripe.postman_collection.json | 114 +++++++++--------- .../volt.postman_collection.json | 10 +- 14 files changed, 92 insertions(+), 92 deletions(-) diff --git a/postman/collection-json/adyen_uk.postman_collection.json b/postman/collection-json/adyen_uk.postman_collection.json index bae84c62f2a3..7b4f9b0a7da6 100644 --- a/postman/collection-json/adyen_uk.postman_collection.json +++ b/postman/collection-json/adyen_uk.postman_collection.json @@ -728,7 +728,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"routing\":{\"type\":\"single\",\"data\":\"adyen\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -1313,7 +1313,7 @@ "language": "json" } }, - "raw": "{\"amount\":1100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"routing\":{\"type\":\"single\",\"data\":\"adyen\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":1100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11093,7 +11093,7 @@ "language": "json" } }, - "raw": "{\"amount\":14100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":14100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"routing\":{\"type\":\"single\",\"data\":\"adyen\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":14100,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":14100,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"gift_card\",\"payment_method_type\":\"givex\",\"payment_method_data\":{\"gift_card\":{\"givex\":{\"number\":\"6364530000000000\",\"cvc\":\"122222\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/authorizedotnet.postman_collection.json b/postman/collection-json/authorizedotnet.postman_collection.json index b07503cd37f6..00ecaec22605 100644 --- a/postman/collection-json/authorizedotnet.postman_collection.json +++ b/postman/collection-json/authorizedotnet.postman_collection.json @@ -974,7 +974,7 @@ "language": "json" } }, - "raw": "{\"amount\":7003,\"currency\":\"USD\",\"confirm\":true,\"routing\":{\"data\":\"authorizedotnet\",\"type\":\"single\"},\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"370000000000002\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"900\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":7003,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"370000000000002\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"900\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2436,7 +2436,7 @@ "language": "json" } }, - "raw": "{\"amount\":7003,\"currency\":\"USD\",\"confirm\":true,\"routing\":{\"data\":\"authorizedotnet\",\"type\":\"single\"},\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"370000000000002\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"900\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":7003,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"370000000000002\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"900\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/bankofamerica.postman_collection.json b/postman/collection-json/bankofamerica.postman_collection.json index e36d7f915aa8..2a3238bb8fd0 100644 --- a/postman/collection-json/bankofamerica.postman_collection.json +++ b/postman/collection-json/bankofamerica.postman_collection.json @@ -2186,7 +2186,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"bankofamerica\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2424,7 +2424,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2675,7 +2675,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"24\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -3125,7 +3125,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"abcd\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"abcd\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"bankofamerica\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"abcd\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"abcd\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -3525,7 +3525,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"3566111111111113\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"3566111111111113\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -3917,7 +3917,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"3566111111111113\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"3566111111111113\",\"card_exp_month\":\"12\",\"card_exp_year\":\"30\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/bluesnap.postman_collection.json b/postman/collection-json/bluesnap.postman_collection.json index bbbc1d7859aa..7f83829a35d0 100644 --- a/postman/collection-json/bluesnap.postman_collection.json +++ b/postman/collection-json/bluesnap.postman_collection.json @@ -4338,7 +4338,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/checkout.postman_collection.json b/postman/collection-json/checkout.postman_collection.json index b0f120bc33c7..1eee3ce85226 100644 --- a/postman/collection-json/checkout.postman_collection.json +++ b/postman/collection-json/checkout.postman_collection.json @@ -9039,7 +9039,7 @@ "language": "json" } }, - "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"checkout\"}}" + "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -9936,7 +9936,7 @@ "language": "json" } }, - "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"checkout\"}}" + "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10439,7 +10439,7 @@ "language": "json" } }, - "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"checkout\"}}" + "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10958,7 +10958,7 @@ "language": "json" } }, - "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"checkout\"}}" + "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11309,7 +11309,7 @@ "language": "json" } }, - "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"checkout\"}}" + "raw": "{\"amount\":6000,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual_multiple\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/cybersource.postman_collection.json b/postman/collection-json/cybersource.postman_collection.json index 139717023011..b877aaa56369 100644 --- a/postman/collection-json/cybersource.postman_collection.json +++ b/postman/collection-json/cybersource.postman_collection.json @@ -7232,7 +7232,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/forte.postman_collection.json b/postman/collection-json/forte.postman_collection.json index 79e0bd07bf21..95e5b82f7961 100644 --- a/postman/collection-json/forte.postman_collection.json +++ b/postman/collection-json/forte.postman_collection.json @@ -516,7 +516,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"routing\":{\"type\":\"single\",\"data\":\"forte\"},\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -745,7 +745,7 @@ "language": "json" } }, - "raw": "{\"amount\":0,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"routing\":{\"type\":\"single\",\"data\":\"forte\"},\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":0,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\",\"last_name\":\"Fix\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/hyperswitch.postman_collection.json b/postman/collection-json/hyperswitch.postman_collection.json index 7c199c9e4753..14e280c7c6c8 100644 --- a/postman/collection-json/hyperswitch.postman_collection.json +++ b/postman/collection-json/hyperswitch.postman_collection.json @@ -10211,7 +10211,7 @@ "language": "json" } }, - "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":false,\"routing\":{\"type\":\"single\",\"data\":\"stripe\"},\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/multisafepay.postman_collection.json b/postman/collection-json/multisafepay.postman_collection.json index fe8efb5ad0f9..66ea1f65ffce 100644 --- a/postman/collection-json/multisafepay.postman_collection.json +++ b/postman/collection-json/multisafepay.postman_collection.json @@ -525,7 +525,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"routing\":{\"type\":\"single\",\"data\":\"multisafepay\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2078,7 +2078,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"routing\":{\"type\":\"single\",\"data\":\"multisafepay\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"someone\",\"last_name\":\"happy\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/nmi.postman_collection.json b/postman/collection-json/nmi.postman_collection.json index 1e48b4a86468..49a99fe3ffea 100644 --- a/postman/collection-json/nmi.postman_collection.json +++ b/postman/collection-json/nmi.postman_collection.json @@ -9101,7 +9101,7 @@ "language": "json" } }, - "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"01\",\"card_exp_year\":\"69\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"John\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"John\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"01\",\"card_exp_year\":\"69\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"John\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"John\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -9769,7 +9769,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"12345\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"12345\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -12321,7 +12321,7 @@ "language": "json" } }, - "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"10\",\"card_exp_year\":\"69\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":\"{{random_number}}\",\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":\"{{random_number}}\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"10\",\"card_exp_year\":\"69\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/paypal.postman_collection.json b/postman/collection-json/paypal.postman_collection.json index 58368b3b856e..58d9cec1c750 100644 --- a/postman/collection-json/paypal.postman_collection.json +++ b/postman/collection-json/paypal.postman_collection.json @@ -805,7 +805,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"surcharge_details\":{\"surcharge_amount\":5,\"tax_amount\":5},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"paypal\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"surcharge_details\":{\"surcharge_amount\":5,\"tax_amount\":5},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"PiX\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/prophetpay.postman_collection.json b/postman/collection-json/prophetpay.postman_collection.json index bd946301f55a..0b8c0a5fe48c 100644 --- a/postman/collection-json/prophetpay.postman_collection.json +++ b/postman/collection-json/prophetpay.postman_collection.json @@ -515,7 +515,7 @@ "language": "json" } }, - "raw": "{\"amount\":10000,\"currency\":\"USD\",\"confirm\":true,\"amount_to_capture\":10000,\"business_country\":\"US\",\"customer_id\":\"not_a_rick_roll\",\"return_url\":\"https://www.google.com\",\"payment_method\":\"card_redirect\",\"payment_method_type\":\"card_redirect\",\"payment_method_data\":{\"card_redirect\":{\"card_redirect\":{}}},\"routing\":{\"type\":\"single\",\"data\":\"prophetpay\"}}" + "raw": "{\"amount\":10000,\"currency\":\"USD\",\"confirm\":true,\"amount_to_capture\":10000,\"business_country\":\"US\",\"customer_id\":\"not_a_rick_roll\",\"return_url\":\"https://www.google.com\",\"payment_method\":\"card_redirect\",\"payment_method_type\":\"card_redirect\",\"payment_method_data\":{\"card_redirect\":{\"card_redirect\":{}}}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -743,7 +743,7 @@ "language": "json" } }, - "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":true,\"amount_to_capture\":8000,\"business_country\":\"US\",\"customer_id\":\"not_a_rick_roll\",\"return_url\":\"https://www.google.com\",\"payment_method\":\"card_redirect\",\"payment_method_type\":\"card_redirect\",\"payment_method_data\":{\"card_redirect\":{\"card_redirect\":{}}},\"routing\":{\"type\":\"single\",\"data\":\"prophetpay\"}}" + "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":true,\"amount_to_capture\":8000,\"business_country\":\"US\",\"customer_id\":\"not_a_rick_roll\",\"return_url\":\"https://www.google.com\",\"payment_method\":\"card_redirect\",\"payment_method_type\":\"card_redirect\",\"payment_method_data\":{\"card_redirect\":{\"card_redirect\":{}}}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -994,7 +994,7 @@ "language": "json" } }, - "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":false,\"amount_to_capture\":8000,\"business_country\":\"US\",\"customer_id\":\"not_a_rick_roll\",\"return_url\":\"https://www.google.com\",\"routing\":{\"type\":\"single\",\"data\":\"prophetpay\"}}" + "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":false,\"amount_to_capture\":8000,\"business_country\":\"US\",\"customer_id\":\"not_a_rick_roll\",\"return_url\":\"https://www.google.com\"}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/stripe.postman_collection.json b/postman/collection-json/stripe.postman_collection.json index 0f7aa535a72a..c6331002fe3f 100644 --- a/postman/collection-json/stripe.postman_collection.json +++ b/postman/collection-json/stripe.postman_collection.json @@ -2493,7 +2493,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"},\"email\":\"example@example.com\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"},\"email\":\"example@example.com\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":{\"connector\":\"stripe\",\"merchant_connector_id\":\"{{merchant_connector_id}}\"}}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -2757,7 +2757,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":{\"connector\":\"stripe\",\"merchant_connector_id\":\"{{merchant_connector_id}}\"}}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -3593,7 +3593,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -4295,7 +4295,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -4542,7 +4542,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -4825,7 +4825,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -5454,7 +5454,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -6040,7 +6040,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -6846,7 +6846,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -7915,7 +7915,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -8315,7 +8315,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -8707,7 +8707,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -9099,7 +9099,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -9467,7 +9467,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"business_country\":\"US\",\"business_label\":\"default\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"business_country\":\"US\",\"business_label\":\"default\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -9709,7 +9709,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10148,7 +10148,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000009995\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000009995\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10419,7 +10419,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -10850,7 +10850,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11620,7 +11620,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -11897,7 +11897,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"mandate_id\":\"{{mandate_id}}\",\"off_session\":true,\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"mandate_id\":\"{{mandate_id}}\",\"off_session\":true,\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -12181,7 +12181,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -12468,7 +12468,7 @@ "language": "json" } }, - "raw": "{\"amount\":6570,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6570,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"mandate_id\":\"{{mandate_id}}\",\"off_session\":true,\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6570,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6570,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"mandate_id\":\"{{mandate_id}}\",\"off_session\":true,\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -12915,7 +12915,7 @@ "language": "json" } }, - "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":8000,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":8000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":8000,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -13345,7 +13345,7 @@ "language": "json" } }, - "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"ES\"},\"email\":\"narayan@example.com\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"SE\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"ES\"},\"email\":\"narayan@example.com\"},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"SE\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -13775,7 +13775,7 @@ "language": "json" } }, - "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"US\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":7000,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"first_name\":\"John\",\"last_name\":\"Doe\",\"country\":\"US\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"order_details\":{\"product_name\":\"Socks\",\"amount\":7000,\"quantity\":1}}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -14205,7 +14205,7 @@ "language": "json" } }, - "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"NL\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"NL\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -14635,7 +14635,7 @@ "language": "json" } }, - "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"DE\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"DE\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -15065,7 +15065,7 @@ "language": "json" } }, - "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"AT\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":1000,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":1000,\"customer_id\":\"StripeCustomer\",\"email\":\"abcdef123@gmail.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"first_name\":\"John\",\"last_name\":\"Doe\",\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"AT\"}},\"browser_info\":{\"user_agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\"accept_header\":\"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\",\"language\":\"nl-NL\",\"color_depth\":24,\"screen_height\":723,\"screen_width\":1536,\"time_zone\":0,\"java_enabled\":true,\"java_script_enabled\":true,\"ip_address\":\"127.0.0.1\"},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"John\",\"last_name\":\"Doe\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -15495,7 +15495,7 @@ "language": "json" } }, - "raw": "{\"amount\":800,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":800,\"customer_id\":\"poll\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://hs-payments-test.netlify.app/payments\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"HarrisonStreet\",\"line3\":\"HarrisonStreet\",\"city\":\"SanFransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Swangi\",\"last_name\":\"Kumari\"},\"email\":\"guest@example.com\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":800,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":800,\"customer_id\":\"poll\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://hs-payments-test.netlify.app/payments\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"HarrisonStreet\",\"line3\":\"HarrisonStreet\",\"city\":\"SanFransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Swangi\",\"last_name\":\"Kumari\"},\"email\":\"guest@example.com\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -15945,7 +15945,7 @@ "language": "json" } }, - "raw": "{\"amount\":1800,\"currency\":\"USD\",\"confirm\":true,\"business_label\":\"default\",\"capture_method\":\"automatic\",\"connector\":[\"stripe\"],\"customer_id\":\"klarna\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"authentication_type\":\"three_ds\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"return_url\":\"https://google.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"setup_future_usage\":\"off_session\",\"business_country\":\"US\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"mandate_type\":{\"single_use\":{\"amount\":6540,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"bank_debit\",\"payment_method_type\":\"ach\",\"payment_method_data\":{\"bank_debit\":{\"ach_bank_debit\":{\"billing_details\":{\"name\":\"John Doe\",\"email\":\"johndoe@example.com\"},\"account_number\":\"000123456789\",\"routing_number\":\"110000000\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"HarrisonStreet\",\"line3\":\"HarrisonStreet\",\"city\":\"SanFransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Swangi\",\"last_name\":\"Kumari\"}},\"metadata\":{\"order_details\":{\"product_name\":\"Apple iphone 15\",\"quantity\":1,\"amount\":1800,\"account_name\":\"transaction_processing\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":1800,\"currency\":\"USD\",\"confirm\":true,\"business_label\":\"default\",\"capture_method\":\"automatic\",\"connector\":[\"stripe\"],\"customer_id\":\"klarna\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"authentication_type\":\"three_ds\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"return_url\":\"https://google.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"setup_future_usage\":\"off_session\",\"business_country\":\"US\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"mandate_type\":{\"single_use\":{\"amount\":6540,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"online\",\"accepted_at\":\"2022-09-10T10:11:12Z\",\"online\":{\"ip_address\":\"123.32.25.123\",\"user_agent\":\"Mozilla/5.0 (Linux; Android 12; SM-S906N Build/QP1A.190711.020; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/80.0.3987.119 Mobile Safari/537.36\"}},\"payment_method\":\"bank_debit\",\"payment_method_type\":\"ach\",\"payment_method_data\":{\"bank_debit\":{\"ach_bank_debit\":{\"billing_details\":{\"name\":\"John Doe\",\"email\":\"johndoe@example.com\"},\"account_number\":\"000123456789\",\"routing_number\":\"110000000\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"HarrisonStreet\",\"line3\":\"HarrisonStreet\",\"city\":\"SanFransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Swangi\",\"last_name\":\"Kumari\"}},\"metadata\":{\"order_details\":{\"product_name\":\"Apple iphone 15\",\"quantity\":1,\"amount\":1800,\"account_name\":\"transaction_processing\"}}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -16187,7 +16187,7 @@ "language": "json" } }, - "raw": "{\"amount\":800,\"currency\":\"USD\",\"confirm\":false,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":800,\"customer_id\":\"poll\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":800,\"currency\":\"USD\",\"confirm\":false,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":800,\"customer_id\":\"poll\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"statement_descriptor_name\":\"Juspay\",\"statement_descriptor_suffix\":\"Router\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -16611,7 +16611,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -17215,7 +17215,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -17627,7 +17627,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_1234\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_1234\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4111111111111111\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"737\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -17827,7 +17827,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -18341,7 +18341,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_1234\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"stripesavecard_1234\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -18541,7 +18541,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -18807,7 +18807,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"adyensavecard\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"credit\",\"payment_method_data\":{\"card\":{\"card_number\":\"371449635398431\",\"card_exp_month\":\"03\",\"card_exp_year\":\"2030\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"7373\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -19161,7 +19161,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"{{customer_id}}\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -19967,7 +19967,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -20227,7 +20227,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"business_country\":\"US\",\"business_label\":\"default\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"business_country\":\"US\",\"business_label\":\"default\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -20485,7 +20485,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrisoff Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Narayan\",\"last_name\":\"Doe\"},\"email\":\"example@juspay.in\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"payment_method\":\"card\",\"payment_method_type\":\"debit\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"26\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrisoff Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"Narayan\",\"last_name\":\"Doe\"},\"email\":\"example@juspay.in\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -20732,7 +20732,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"business_country\":\"US\",\"business_label\":\"default\",\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"bernard123\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"setup_future_usage\":\"on_session\",\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -22279,7 +22279,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -22781,7 +22781,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"united states\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -22904,7 +22904,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"2023\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"01\",\"card_exp_year\":\"2023\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -23037,7 +23037,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"2022\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"2022\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -23160,7 +23160,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"12345\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"123456\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"12345\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -23283,7 +23283,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -23573,7 +23573,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -23953,7 +23953,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -24214,7 +24214,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -24471,7 +24471,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"manual\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4000000000003063\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -24851,7 +24851,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -25188,7 +25188,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -25551,7 +25551,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":6540,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"payment_method\":\"card\",\"payment_method_data\":{\"card\":{\"card_number\":\"4242424242424242\",\"card_exp_month\":\"10\",\"card_exp_year\":\"25\",\"card_holder_name\":\"joseph Doe\",\"card_cvc\":\"123\"}},\"setup_future_usage\":\"off_session\",\"mandate_data\":{\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"mandate_type\":{\"single_use\":{\"amount\":7000,\"currency\":\"USD\"}}},\"customer_acceptance\":{\"acceptance_type\":\"offline\",\"accepted_at\":\"1963-05-03T04:07:52.723Z\",\"online\":{\"ip_address\":\"127.0.0.1\",\"user_agent\":\"amet irure esse\"}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -25812,7 +25812,7 @@ "language": "json" } }, - "raw": "{\"amount\":8040,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"mandate_id\":\"{{mandate_id}}\",\"off_session\":true,\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"stripe\"}}" + "raw": "{\"amount\":8040,\"currency\":\"USD\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+65\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://duck.com\",\"mandate_id\":\"{{mandate_id}}\",\"off_session\":true,\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", diff --git a/postman/collection-json/volt.postman_collection.json b/postman/collection-json/volt.postman_collection.json index c34d77fea039..3b996480472b 100644 --- a/postman/collection-json/volt.postman_collection.json +++ b/postman/collection-json/volt.postman_collection.json @@ -516,7 +516,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"open_banking_uk\",\"payment_method_data\":{\"bank_redirect\":{\"open_banking_uk\":{\"issuer\":\"citi\",\"country\":\"GB\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"volt\"}}" + "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"open_banking_uk\",\"payment_method_data\":{\"bank_redirect\":{\"open_banking_uk\":{\"issuer\":\"citi\",\"country\":\"GB\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -754,7 +754,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"open_banking_uk\",\"payment_method_data\":{\"bank_redirect\":{\"open_banking_uk\":{\"issuer\":\"citi\",\"country\":\"GB\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"volt\"}}" + "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":true,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"open_banking_uk\",\"payment_method_data\":{\"bank_redirect\":{\"open_banking_uk\":{\"issuer\":\"citi\",\"country\":\"GB\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -1005,7 +1005,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"open_banking_uk\",\"payment_method_data\":{\"bank_redirect\":{\"open_banking_uk\":{\"issuer\":\"citi\",\"country\":\"GB\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"volt\"}}" + "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"payment_method\":\"bank_redirect\",\"payment_method_type\":\"open_banking_uk\",\"payment_method_data\":{\"bank_redirect\":{\"open_banking_uk\":{\"issuer\":\"citi\",\"country\":\"GB\"}}},\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"joseph\",\"last_name\":\"Doe\"},\"phone\":{\"number\":\"9123456789\",\"country_code\":\"+91\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -1454,7 +1454,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"singh\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"volt\"}}" + "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"singh\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", @@ -1854,7 +1854,7 @@ "language": "json" } }, - "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"singh\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"},\"routing\":{\"type\":\"single\",\"data\":\"volt\"}}" + "raw": "{\"amount\":6540,\"currency\":\"EUR\",\"confirm\":false,\"capture_method\":\"automatic\",\"capture_on\":\"2022-09-10T10:11:12Z\",\"amount_to_capture\":6540,\"customer_id\":\"StripeCustomer\",\"email\":\"guest@example.com\",\"name\":\"John Doe\",\"phone\":\"999999999\",\"phone_country_code\":\"+1\",\"description\":\"Its my first payment request\",\"authentication_type\":\"no_three_ds\",\"return_url\":\"https://google.com\",\"billing\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\",\"last_name\":\"singh\"}},\"shipping\":{\"address\":{\"line1\":\"1467\",\"line2\":\"Harrison Street\",\"line3\":\"Harrison Street\",\"city\":\"San Fransico\",\"state\":\"California\",\"zip\":\"94122\",\"country\":\"US\",\"first_name\":\"sundari\"}},\"statement_descriptor_name\":\"joseph\",\"statement_descriptor_suffix\":\"JS\",\"metadata\":{\"udf1\":\"value1\",\"new_customer\":\"true\",\"login_date\":\"2019-09-10T10:11:12Z\"}}" }, "url": { "raw": "{{baseUrl}}/payments", From e098415bb694f76839e3b9c59f218e353cdd15c8 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 05:04:54 +0000 Subject: [PATCH 20/26] chore(version): 2024.07.19.0 --- CHANGELOG.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 421ef15d409a..81e590a7726d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,40 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.07.19.0 + +### Features + +- **connector:** [Itau Bank] Template for payment flows ([#5304](https://github.com/juspay/hyperswitch/pull/5304)) ([`ef1418f`](https://github.com/juspay/hyperswitch/commit/ef1418f978835a8df149181bc5e19053775490f2)) + +### Bug Fixes + +- **core:** [payouts] failure of payout retrieve when token is expired ([#5362](https://github.com/juspay/hyperswitch/pull/5362)) ([`817d06c`](https://github.com/juspay/hyperswitch/commit/817d06c7faa14493674931ba51ab6c32769602d1)) + +### Refactors + +- **blocklist:** Change primary key of blocklist table ([#5356](https://github.com/juspay/hyperswitch/pull/5356)) ([`93047ae`](https://github.com/juspay/hyperswitch/commit/93047ae616b6a5240c2be13f8c5267e5fa616eab)) +- **merchant_account:** Change primary key for merchant account ([#5327](https://github.com/juspay/hyperswitch/pull/5327)) ([`a0c367e`](https://github.com/juspay/hyperswitch/commit/a0c367ee8c7f04cfdf1f9d4863447f24279a537f)) +- **router:** Remove id dependency from merchant connector account, dispute and mandate ([#5330](https://github.com/juspay/hyperswitch/pull/5330)) ([`6d74527`](https://github.com/juspay/hyperswitch/commit/6d74527f4428c8cb61e89d59385bfb2dd8056c2e)) +- **routing:** Remove backwards compatibility for the routing crate ([#3015](https://github.com/juspay/hyperswitch/pull/3015)) ([`78a7804`](https://github.com/juspay/hyperswitch/commit/78a7804b9c8b4db881b112fc72e31cfd3e97a82d)) + +### Documentation + +- Updating Error codes for documentation purposes ([#5314](https://github.com/juspay/hyperswitch/pull/5314)) ([`fe14336`](https://github.com/juspay/hyperswitch/commit/fe14336f78b15e948b10d09b197fb1d529939b5c)) + +### Miscellaneous Tasks + +- **postman:** Update Postman collection files ([`93dd60c`](https://github.com/juspay/hyperswitch/commit/93dd60c9ab8180ed7688959e32a3c2730c2bfad2)) +- Increasing log coverage for payment method list ([#5042](https://github.com/juspay/hyperswitch/pull/5042)) ([`edb581e`](https://github.com/juspay/hyperswitch/commit/edb581e8e3307714c1ebc3dec65fc73aeb2d7cb0)) + +### Build System / Dependencies + +- Remove unused dependencies ([#5343](https://github.com/juspay/hyperswitch/pull/5343)) ([`7f582e4`](https://github.com/juspay/hyperswitch/commit/7f582e4737c1c7dfe906e7d01de239e131511f84)) + +**Full Changelog:** [`2024.07.18.0...2024.07.19.0`](https://github.com/juspay/hyperswitch/compare/2024.07.18.0...2024.07.19.0) + +- - - + ## 2024.07.18.0 ### Features From c698921c417da4f6f74887224818ccb5d92b9fc3 Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Fri, 19 Jul 2024 13:05:27 +0530 Subject: [PATCH 21/26] refactor(core): change primary key of refund table (#5367) --- crates/diesel_models/src/schema.rs | 2 +- crates/diesel_models/src/schema_v2.rs | 2 +- .../down.sql | 5 +++++ .../up.sql | 12 ++++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 migrations/2024-07-19-044034_change_primary_key_for_refund/down.sql create mode 100644 migrations/2024-07-19-044034_change_primary_key_for_refund/up.sql diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 171e1cfe2e34..4b5f4a8ad49f 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1099,7 +1099,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - refund (id) { + refund (merchant_id, refund_id) { id -> Int4, #[max_length = 64] internal_reference_id -> Varchar, diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index d16ddcd65181..a2e35a6d6a98 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -1097,7 +1097,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - refund (id) { + refund (merchant_id, refund_id) { id -> Int4, #[max_length = 64] internal_reference_id -> Varchar, diff --git a/migrations/2024-07-19-044034_change_primary_key_for_refund/down.sql b/migrations/2024-07-19-044034_change_primary_key_for_refund/down.sql new file mode 100644 index 000000000000..42faa31536f5 --- /dev/null +++ b/migrations/2024-07-19-044034_change_primary_key_for_refund/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE refund DROP CONSTRAINT refund_pkey; + +ALTER TABLE refund +ADD PRIMARY KEY (id); diff --git a/migrations/2024-07-19-044034_change_primary_key_for_refund/up.sql b/migrations/2024-07-19-044034_change_primary_key_for_refund/up.sql new file mode 100644 index 000000000000..bbd6ef735da4 --- /dev/null +++ b/migrations/2024-07-19-044034_change_primary_key_for_refund/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here +-- The below query will lock the refund table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query should be run after the new version of application is deployed +ALTER TABLE refund DROP CONSTRAINT refund_pkey; + +-- Use the `merchant_id, refund_id` columns as primary key +-- These are already unique, not null columns +-- So this query should not fail for not null or duplicate value reasons +ALTER TABLE refund +ADD PRIMARY KEY (merchant_id, refund_id); From 33298b38081c46fe4ee38f8ad6ddffd2b98a1d5c Mon Sep 17 00:00:00 2001 From: Arjun Karthik Date: Fri, 19 Jul 2024 13:08:58 +0530 Subject: [PATCH 22/26] feat: encryption service integration to support batch encryption and decryption (#5164) Co-authored-by: dracarys18 Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- Cargo.lock | 3 + crates/api_models/src/customers.rs | 90 +- crates/api_models/src/payments.rs | 73 +- crates/common_utils/Cargo.toml | 2 + crates/common_utils/src/consts.rs | 2 + .../src/encryption.rs | 62 +- crates/common_utils/src/keymanager.rs | 72 +- crates/common_utils/src/lib.rs | 3 + crates/common_utils/src/transformers.rs | 15 + crates/common_utils/src/types/keymanager.rs | 440 +++++++++- crates/diesel_models/Cargo.toml | 1 + crates/diesel_models/src/address.rs | 68 +- crates/diesel_models/src/business_profile.rs | 4 +- crates/diesel_models/src/customers.rs | 4 +- crates/diesel_models/src/events.rs | 40 +- crates/diesel_models/src/lib.rs | 1 - crates/diesel_models/src/merchant_account.rs | 4 +- .../src/merchant_connector_account.rs | 4 +- .../diesel_models/src/merchant_key_store.rs | 4 +- crates/diesel_models/src/payment_intent.rs | 4 +- crates/diesel_models/src/payment_method.rs | 4 +- crates/diesel_models/src/user.rs | 6 +- .../src/user_authentication_method.rs | 3 +- crates/diesel_models/src/user_key_store.rs | 3 +- crates/hyperswitch_domain_models/Cargo.toml | 4 +- .../src/behaviour.rs | 23 +- .../src/merchant_account.rs | 13 +- .../src/merchant_key_store.rs | 6 +- .../src/payments/payment_attempt.rs | 17 +- .../src/payments/payment_intent.rs | 11 +- .../src/type_encryption.rs | 766 +++++++++++++++++- crates/router/Cargo.toml | 7 +- crates/router/src/bin/scheduler.rs | 6 +- crates/router/src/consts.rs | 6 +- crates/router/src/core/admin.rs | 332 +++++--- crates/router/src/core/api_keys.rs | 1 + .../core/apple_pay_certificates_migration.rs | 8 +- crates/router/src/core/blocklist/utils.rs | 1 + crates/router/src/core/cards_info.rs | 2 +- crates/router/src/core/conditional_config.rs | 6 +- crates/router/src/core/customers.rs | 196 +++-- crates/router/src/core/disputes.rs | 2 + crates/router/src/core/encryption.rs | 2 +- crates/router/src/core/files/helpers.rs | 1 + crates/router/src/core/fraud_check.rs | 16 +- .../router/src/core/fraud_check/operation.rs | 3 +- .../fraud_check/operation/fraud_check_post.rs | 5 +- .../fraud_check/operation/fraud_check_pre.rs | 4 +- crates/router/src/core/locker_migration.rs | 7 +- crates/router/src/core/mandate/helpers.rs | 1 + crates/router/src/core/payment_link.rs | 2 + crates/router/src/core/payment_methods.rs | 1 + .../router/src/core/payment_methods/cards.rs | 355 ++++---- .../src/core/payment_methods/validator.rs | 1 + crates/router/src/core/payments.rs | 22 +- crates/router/src/core/payments/helpers.rs | 413 +++++----- crates/router/src/core/payments/operations.rs | 17 +- .../payments/operations/payment_approve.rs | 15 +- .../payments/operations/payment_cancel.rs | 15 +- .../payments/operations/payment_capture.rs | 7 +- .../operations/payment_complete_authorize.rs | 13 +- .../payments/operations/payment_confirm.rs | 40 +- .../payments/operations/payment_create.rs | 40 +- .../payments/operations/payment_reject.rs | 8 +- .../payments/operations/payment_response.rs | 19 +- .../payments/operations/payment_session.rs | 14 +- .../core/payments/operations/payment_start.rs | 12 +- .../payments/operations/payment_status.rs | 29 +- .../payments/operations/payment_update.rs | 17 +- .../payments_incremental_authorization.rs | 15 +- crates/router/src/core/payments/retry.rs | 1 + crates/router/src/core/payments/routing.rs | 1 + .../router/src/core/payments/tokenization.rs | 20 +- crates/router/src/core/payout_link.rs | 7 +- crates/router/src/core/payouts.rs | 16 +- crates/router/src/core/payouts/helpers.rs | 70 +- crates/router/src/core/pm_auth.rs | 33 +- crates/router/src/core/refunds.rs | 30 +- crates/router/src/core/routing.rs | 2 +- crates/router/src/core/routing/helpers.rs | 12 +- .../src/core/surcharge_decision_config.rs | 6 +- crates/router/src/core/user.rs | 47 +- .../src/core/user/dashboard_metadata.rs | 2 + crates/router/src/core/user/sample_data.rs | 8 +- crates/router/src/core/verification.rs | 8 +- crates/router/src/core/verification/utils.rs | 4 + crates/router/src/core/webhooks/incoming.rs | 3 +- crates/router/src/core/webhooks/outgoing.rs | 25 +- .../src/core/webhooks/webhook_events.rs | 41 +- crates/router/src/db/address.rs | 164 +++- crates/router/src/db/customers.rs | 103 ++- crates/router/src/db/events.rs | 170 +++- crates/router/src/db/kafka_store.rs | 165 +++- crates/router/src/db/merchant_account.rs | 90 +- .../src/db/merchant_connector_account.rs | 190 ++++- crates/router/src/db/merchant_key_store.rs | 87 +- crates/router/src/db/user/sample_data.rs | 22 +- crates/router/src/db/user_key_store.rs | 28 +- crates/router/src/lib.rs | 6 +- crates/router/src/routes/app.rs | 4 + crates/router/src/routes/payment_methods.rs | 8 +- crates/router/src/routes/recon.rs | 21 +- crates/router/src/services/api.rs | 17 +- crates/router/src/services/api/client.rs | 14 +- crates/router/src/services/authentication.rs | 44 +- crates/router/src/types/api/admin.rs | 12 +- crates/router/src/types/api/mandates.rs | 3 +- crates/router/src/types/domain/address.rs | 78 +- crates/router/src/types/domain/customer.rs | 67 +- crates/router/src/types/domain/event.rs | 68 +- .../domain/merchant_connector_account.rs | 16 +- crates/router/src/types/domain/types.rs | 3 +- crates/router/src/types/domain/user.rs | 36 +- .../router/src/types/domain/user_key_store.rs | 6 +- crates/router/src/utils.rs | 258 +++--- .../router/src/utils/connector_onboarding.rs | 3 + crates/router/src/utils/user.rs | 15 +- crates/router/src/utils/user/sample_data.rs | 5 +- crates/router/src/workflows/api_key_expiry.rs | 9 +- .../attach_payout_account_workflow.rs | 5 +- .../src/workflows/outgoing_webhook_retry.rs | 13 +- .../workflows/payment_method_status_update.rs | 5 +- crates/router/src/workflows/payment_sync.rs | 5 +- crates/router/tests/payments.rs | 10 +- crates/router/tests/payments2.rs | 10 +- .../src/mock_db/payment_intent.rs | 10 +- .../src/payments/payment_intent.rs | 98 ++- 127 files changed, 4239 insertions(+), 1378 deletions(-) rename crates/{diesel_models => common_utils}/src/encryption.rs (88%) create mode 100644 crates/common_utils/src/transformers.rs diff --git a/Cargo.lock b/Cargo.lock index 193f19453b9a..66b38d09c1f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2006,6 +2006,7 @@ name = "common_utils" version = "0.1.0" dependencies = [ "async-trait", + "base64 0.22.0", "blake3", "bytes 1.6.0", "common_enums", @@ -2704,6 +2705,7 @@ dependencies = [ "masking", "router_derive", "router_env", + "rustc-hash", "serde", "serde_json", "strum 0.26.2", @@ -3858,6 +3860,7 @@ dependencies = [ "mime", "router_derive", "router_env", + "rustc-hash", "serde", "serde_json", "serde_with", diff --git a/crates/api_models/src/customers.rs b/crates/api_models/src/customers.rs index 37c4cd0f1b86..73fb5264ed95 100644 --- a/crates/api_models/src/customers.rs +++ b/crates/api_models/src/customers.rs @@ -1,5 +1,12 @@ -use common_utils::{crypto, custom_serde, id_type, pii}; -use masking::Secret; +use common_utils::{ + crypto, custom_serde, + encryption::Encryption, + id_type, + pii::{self, EmailStrategy}, + types::keymanager::ToEncryptable, +}; +use euclid::dssa::graph::euclid_graph_prelude::FxHashMap; +use masking::{ExposeInterface, Secret, SwitchStrategy}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; @@ -40,6 +47,85 @@ pub struct CustomerRequest { pub metadata: Option, } +pub struct CustomerRequestWithEmail { + pub name: Option>, + pub email: Option, + pub phone: Option>, +} + +pub struct CustomerRequestWithEncryption { + pub name: Option, + pub phone: Option, + pub email: Option, +} + +pub struct EncryptableCustomer { + pub name: crypto::OptionalEncryptableName, + pub phone: crypto::OptionalEncryptablePhone, + pub email: crypto::OptionalEncryptableEmail, +} + +impl ToEncryptable, Encryption> + for CustomerRequestWithEncryption +{ + fn to_encryptable(self) -> FxHashMap { + let mut map = FxHashMap::with_capacity_and_hasher(3, Default::default()); + self.name.map(|x| map.insert("name".to_string(), x)); + self.phone.map(|x| map.insert("phone".to_string(), x)); + self.email.map(|x| map.insert("email".to_string(), x)); + map + } + + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableCustomer { + name: hashmap.remove("name"), + phone: hashmap.remove("phone"), + email: hashmap.remove("email").map(|email| { + let encryptable: crypto::Encryptable> = + crypto::Encryptable::new( + email.clone().into_inner().switch_strategy(), + email.into_encrypted(), + ); + encryptable + }), + }) + } +} + +impl ToEncryptable, Secret> + for CustomerRequestWithEmail +{ + fn to_encryptable(self) -> FxHashMap> { + let mut map = FxHashMap::with_capacity_and_hasher(3, Default::default()); + self.name.map(|x| map.insert("name".to_string(), x)); + self.phone.map(|x| map.insert("phone".to_string(), x)); + self.email + .map(|x| map.insert("email".to_string(), x.expose().switch_strategy())); + map + } + + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableCustomer { + name: hashmap.remove("name"), + email: hashmap.remove("email").map(|email| { + let encryptable: crypto::Encryptable> = + crypto::Encryptable::new( + email.clone().into_inner().switch_strategy(), + email.into_encrypted(), + ); + encryptable + }), + phone: hashmap.remove("phone"), + }) + } +} + #[derive(Debug, Clone, Serialize, ToSchema)] pub struct CustomerResponse { /// The identifier for the customer object diff --git a/crates/api_models/src/payments.rs b/crates/api_models/src/payments.rs index 619f345a3336..5ac8d0edcc3e 100644 --- a/crates/api_models/src/payments.rs +++ b/crates/api_models/src/payments.rs @@ -11,10 +11,11 @@ use common_utils::{ ext_traits::{ConfigExt, Encode}, hashing::HashedString, id_type, - pii::{self, Email}, - types::{MinorUnit, StringMajorUnit}, + pii::{self, Email, EmailStrategy}, + types::{keymanager::ToEncryptable, MinorUnit, StringMajorUnit}, }; -use masking::{PeekInterface, Secret, WithType}; +use euclid::dssa::graph::euclid_graph_prelude::FxHashMap; +use masking::{ExposeInterface, PeekInterface, Secret, SwitchStrategy, WithType}; use router_derive::Setter; use serde::{ de::{self, Unexpected, Visitor}, @@ -3187,6 +3188,72 @@ impl AddressDetails { } } +pub struct AddressDetailsWithPhone { + pub address: Option, + pub phone_number: Option>, + pub email: Option, +} + +pub struct EncryptableAddressDetails { + pub line1: crypto::OptionalEncryptableSecretString, + pub line2: crypto::OptionalEncryptableSecretString, + pub line3: crypto::OptionalEncryptableSecretString, + pub state: crypto::OptionalEncryptableSecretString, + pub zip: crypto::OptionalEncryptableSecretString, + pub first_name: crypto::OptionalEncryptableSecretString, + pub last_name: crypto::OptionalEncryptableSecretString, + pub phone_number: crypto::OptionalEncryptableSecretString, + pub email: crypto::OptionalEncryptableEmail, +} + +impl ToEncryptable, Secret> + for AddressDetailsWithPhone +{ + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult< + EncryptableAddressDetails, + common_utils::errors::ParsingError, + > { + Ok(EncryptableAddressDetails { + line1: hashmap.remove("line1"), + line2: hashmap.remove("line2"), + line3: hashmap.remove("line3"), + state: hashmap.remove("state"), + zip: hashmap.remove("zip"), + first_name: hashmap.remove("first_name"), + last_name: hashmap.remove("last_name"), + phone_number: hashmap.remove("phone_number"), + email: hashmap.remove("email").map(|x| { + let inner: Secret = x.clone().into_inner().switch_strategy(); + crypto::Encryptable::new(inner, x.into_encrypted()) + }), + }) + } + + fn to_encryptable(self) -> FxHashMap> { + let mut map = FxHashMap::with_capacity_and_hasher(9, Default::default()); + self.address.map(|address| { + address.line1.map(|x| map.insert("line1".to_string(), x)); + address.line2.map(|x| map.insert("line2".to_string(), x)); + address.line3.map(|x| map.insert("line3".to_string(), x)); + address.state.map(|x| map.insert("state".to_string(), x)); + address.zip.map(|x| map.insert("zip".to_string(), x)); + address + .first_name + .map(|x| map.insert("first_name".to_string(), x)); + address + .last_name + .map(|x| map.insert("last_name".to_string(), x)); + }); + self.email + .map(|x| map.insert("email".to_string(), x.expose().switch_strategy())); + self.phone_number + .map(|x| map.insert("phone_number".to_string(), x)); + map + } +} + #[derive(Debug, Clone, Default, Eq, PartialEq, ToSchema, serde::Deserialize, serde::Serialize)] pub struct PhoneDetails { /// The contact number diff --git a/crates/common_utils/Cargo.toml b/crates/common_utils/Cargo.toml index df6b44006336..61d96a217375 100644 --- a/crates/common_utils/Cargo.toml +++ b/crates/common_utils/Cargo.toml @@ -10,6 +10,7 @@ license.workspace = true [features] keymanager = ["dep:router_env"] keymanager_mtls = ["reqwest/rustls-tls"] +encryption_service = ["dep:router_env"] signals = ["dep:signal-hook-tokio", "dep:signal-hook", "dep:tokio", "dep:router_env", "dep:futures"] async_ext = ["dep:async-trait", "dep:futures"] logs = ["dep:router_env"] @@ -17,6 +18,7 @@ metrics = ["dep:router_env", "dep:futures"] [dependencies] async-trait = { version = "0.1.79", optional = true } +base64 = "0.22.0" blake3 = { version = "1.5.1", features = ["serde"] } bytes = "1.6.0" diesel = "2.1.5" diff --git a/crates/common_utils/src/consts.rs b/crates/common_utils/src/consts.rs index 848189cd8995..e52d24c9baa1 100644 --- a/crates/common_utils/src/consts.rs +++ b/crates/common_utils/src/consts.rs @@ -99,6 +99,8 @@ pub const MAX_ALLOWED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 64; /// Minimum allowed length for MerchantReferenceId pub const MIN_REQUIRED_MERCHANT_REFERENCE_ID_LENGTH: u8 = 1; +/// General purpose base64 engine +pub const BASE64_ENGINE: base64::engine::GeneralPurpose = base64::engine::general_purpose::STANDARD; /// Regex for matching a domain /// Eg - /// http://www.example.com diff --git a/crates/diesel_models/src/encryption.rs b/crates/common_utils/src/encryption.rs similarity index 88% rename from crates/diesel_models/src/encryption.rs rename to crates/common_utils/src/encryption.rs index 6c6063c16049..9c566432782c 100644 --- a/crates/diesel_models/src/encryption.rs +++ b/crates/common_utils/src/encryption.rs @@ -1,40 +1,13 @@ -use common_utils::pii::EncryptionStrategy; use diesel::{ backend::Backend, deserialize::{self, FromSql, Queryable}, + expression::AsExpression, serialize::ToSql, - sql_types, AsExpression, + sql_types, }; use masking::Secret; -#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] -#[diesel(sql_type = sql_types::Binary)] -#[repr(transparent)] -pub struct Encryption { - inner: Secret, EncryptionStrategy>, -} - -impl From> for Encryption { - fn from(value: common_utils::crypto::Encryptable) -> Self { - Self::new(value.into_encrypted()) - } -} - -impl Encryption { - pub fn new(item: Secret, EncryptionStrategy>) -> Self { - Self { inner: item } - } - - #[inline] - pub fn into_inner(self) -> Secret, EncryptionStrategy> { - self.inner - } - - #[inline] - pub fn get_inner(&self) -> &Secret, EncryptionStrategy> { - &self.inner - } -} +use crate::{crypto::Encryptable, pii::EncryptionStrategy}; impl FromSql for Encryption where @@ -69,3 +42,32 @@ where Ok(Self { inner: row }) } } + +#[derive(Debug, AsExpression, Clone, serde::Serialize, serde::Deserialize, Eq, PartialEq)] +#[diesel(sql_type = sql_types::Binary)] +#[repr(transparent)] +pub struct Encryption { + inner: Secret, EncryptionStrategy>, +} + +impl From> for Encryption { + fn from(value: Encryptable) -> Self { + Self::new(value.into_encrypted()) + } +} + +impl Encryption { + pub fn new(item: Secret, EncryptionStrategy>) -> Self { + Self { inner: item } + } + + #[inline] + pub fn into_inner(self) -> Secret, EncryptionStrategy> { + self.inner + } + + #[inline] + pub fn get_inner(&self) -> &Secret, EncryptionStrategy> { + &self.inner + } +} diff --git a/crates/common_utils/src/keymanager.rs b/crates/common_utils/src/keymanager.rs index ecdb9b63ad5c..9d6e93150741 100644 --- a/crates/common_utils/src/keymanager.rs +++ b/crates/common_utils/src/keymanager.rs @@ -3,22 +3,26 @@ use core::fmt::Debug; use std::str::FromStr; +use base64::Engine; use error_stack::ResultExt; use http::{HeaderMap, HeaderName, HeaderValue, Method, StatusCode}; -#[cfg(feature = "keymanager_mtls")] -use masking::PeekInterface; +use masking::{PeekInterface, StrongSecret}; use once_cell::sync::OnceCell; use router_env::{instrument, logger, tracing}; use crate::{ + consts::BASE64_ENGINE, errors, types::keymanager::{ - DataKeyCreateResponse, EncryptionCreateRequest, EncryptionTransferRequest, KeyManagerState, + BatchDecryptDataRequest, DataKeyCreateResponse, DecryptDataRequest, + EncryptionCreateRequest, EncryptionTransferRequest, KeyManagerState, + TransientBatchDecryptDataRequest, TransientDecryptDataRequest, }, }; const CONTENT_TYPE: &str = "Content-Type"; static ENCRYPTION_API_CLIENT: OnceCell = OnceCell::new(); +static DEFAULT_ENCRYPTION_VERSION: &str = "v1"; /// Get keymanager client constructed from the url and state #[instrument(skip_all)] @@ -68,7 +72,7 @@ pub async fn send_encryption_request( request_body: T, ) -> errors::CustomResult where - T: serde::Serialize, + T: ConvertRaw, { let client = get_api_encryption_client(state)?; let url = reqwest::Url::parse(&url) @@ -76,7 +80,7 @@ where client .request(method, url) - .json(&request_body) + .json(&ConvertRaw::convert_raw(request_body)?) .headers(headers) .send() .await @@ -94,7 +98,7 @@ pub async fn call_encryption_service( request_body: T, ) -> errors::CustomResult where - T: serde::Serialize + Send + Sync + 'static + Debug, + T: ConvertRaw + Send + Sync + 'static + Debug, R: serde::de::DeserializeOwned, { let url = format!("{}/{endpoint}", &state.url); @@ -152,6 +156,62 @@ where } } +/// Trait to convert the raw data to the required format for encryption service request +pub trait ConvertRaw { + /// Return type of the convert_raw function + type Output: serde::Serialize; + /// Function to convert the raw data to the required format for encryption service request + fn convert_raw(self) -> Result; +} + +impl ConvertRaw for T { + type Output = T; + fn convert_raw(self) -> Result { + Ok(self) + } +} + +impl ConvertRaw for TransientDecryptDataRequest { + type Output = DecryptDataRequest; + fn convert_raw(self) -> Result { + let data = match String::from_utf8(self.data.peek().clone()) { + Ok(data) => data, + Err(_) => { + let data = BASE64_ENGINE.encode(self.data.peek().clone()); + format!("{DEFAULT_ENCRYPTION_VERSION}:{data}") + } + }; + Ok(DecryptDataRequest { + identifier: self.identifier, + data: StrongSecret::new(data), + }) + } +} + +impl ConvertRaw for TransientBatchDecryptDataRequest { + type Output = BatchDecryptDataRequest; + fn convert_raw(self) -> Result { + let data = self + .data + .iter() + .map(|(k, v)| { + let value = match String::from_utf8(v.peek().clone()) { + Ok(data) => data, + Err(_) => { + let data = BASE64_ENGINE.encode(v.peek().clone()); + format!("{DEFAULT_ENCRYPTION_VERSION}:{data}") + } + }; + (k.to_owned(), StrongSecret::new(value)) + }) + .collect(); + Ok(BatchDecryptDataRequest { + data, + identifier: self.identifier, + }) + } +} + /// A function to create the key in keymanager #[instrument(skip_all)] pub async fn create_key_in_key_manager( diff --git a/crates/common_utils/src/lib.rs b/crates/common_utils/src/lib.rs index 9b339fb956e3..e93b0c66ceb1 100644 --- a/crates/common_utils/src/lib.rs +++ b/crates/common_utils/src/lib.rs @@ -13,6 +13,8 @@ pub mod access_token; pub mod consts; pub mod crypto; pub mod custom_serde; +#[allow(missing_docs)] // Todo: add docs +pub mod encryption; pub mod errors; #[allow(missing_docs)] // Todo: add docs pub mod events; @@ -31,6 +33,7 @@ pub mod request; pub mod signals; #[allow(missing_docs)] // Todo: add docs pub mod static_cache; +pub mod transformers; pub mod types; pub mod validation; diff --git a/crates/common_utils/src/transformers.rs b/crates/common_utils/src/transformers.rs new file mode 100644 index 000000000000..3799522e16d4 --- /dev/null +++ b/crates/common_utils/src/transformers.rs @@ -0,0 +1,15 @@ +//! Utilities for converting between foreign types + +/// Trait for converting from one foreign type to another +pub trait ForeignFrom { + /// Convert from a foreign type to the current type + fn foreign_from(from: F) -> Self; +} + +/// Trait for converting from one foreign type to another +pub trait ForeignTryFrom: Sized { + /// Custom error for conversion failure + type Error; + /// Convert from a foreign type to the current type and return an error if the conversion fails + fn foreign_try_from(from: F) -> Result; +} diff --git a/crates/common_utils/src/types/keymanager.rs b/crates/common_utils/src/types/keymanager.rs index 356ccdd48320..5a7b87d0ce09 100644 --- a/crates/common_utils/src/types/keymanager.rs +++ b/crates/common_utils/src/types/keymanager.rs @@ -1,8 +1,24 @@ #![allow(missing_docs)] -#[cfg(feature = "keymanager_mtls")] -use masking::Secret; -use serde::{Deserialize, Serialize}; +use core::fmt; + +use base64::Engine; +use masking::{ExposeInterface, PeekInterface, Secret, Strategy, StrongSecret}; +#[cfg(feature = "encryption_service")] +use router_env::logger; +use rustc_hash::FxHashMap; +use serde::{ + de::{self, Unexpected, Visitor}, + ser, Deserialize, Deserializer, Serialize, +}; + +use crate::{ + consts::BASE64_ENGINE, + crypto::Encryptable, + encryption::Encryption, + errors::{self, CustomResult}, + transformers::{ForeignFrom, ForeignTryFrom}, +}; #[derive(Debug)] pub struct KeyManagerState { @@ -18,6 +34,7 @@ pub struct KeyManagerState { pub enum Identifier { User(String), Merchant(String), + UserAuth(String), } #[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)] @@ -39,3 +56,420 @@ pub struct DataKeyCreateResponse { pub identifier: Identifier, pub key_version: String, } + +#[derive(Serialize, Deserialize, Debug)] +pub struct BatchEncryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: DecryptedDataGroup, +} + +impl From<(Secret, S>, Identifier)> for EncryptDataRequest +where + S: Strategy>, +{ + fn from((secret, identifier): (Secret, S>, Identifier)) -> Self { + Self { + identifier, + data: DecryptedData(StrongSecret::new(secret.expose())), + } + } +} + +impl From<(FxHashMap, S>>, Identifier)> for BatchEncryptDataRequest +where + S: Strategy>, +{ + fn from((map, identifier): (FxHashMap, S>>, Identifier)) -> Self { + let group = map + .into_iter() + .map(|(key, value)| (key, DecryptedData(StrongSecret::new(value.expose())))) + .collect(); + Self { + identifier, + data: DecryptedDataGroup(group), + } + } +} + +impl From<(Secret, Identifier)> for EncryptDataRequest +where + S: Strategy, +{ + fn from((secret, identifier): (Secret, Identifier)) -> Self { + Self { + data: DecryptedData(StrongSecret::new(secret.expose().as_bytes().to_vec())), + identifier, + } + } +} + +impl From<(Secret, Identifier)> for EncryptDataRequest +where + S: Strategy, +{ + fn from((secret, identifier): (Secret, Identifier)) -> Self { + Self { + data: DecryptedData(StrongSecret::new( + secret.expose().to_string().as_bytes().to_vec(), + )), + identifier, + } + } +} + +impl From<(FxHashMap>, Identifier)> + for BatchEncryptDataRequest +where + S: Strategy, +{ + fn from( + (map, identifier): (FxHashMap>, Identifier), + ) -> Self { + let group = map + .into_iter() + .map(|(key, value)| { + ( + key, + DecryptedData(StrongSecret::new( + value.expose().to_string().as_bytes().to_vec(), + )), + ) + }) + .collect(); + Self { + data: DecryptedDataGroup(group), + identifier, + } + } +} + +impl From<(FxHashMap>, Identifier)> for BatchEncryptDataRequest +where + S: Strategy, +{ + fn from((map, identifier): (FxHashMap>, Identifier)) -> Self { + let group = map + .into_iter() + .map(|(key, value)| { + ( + key, + DecryptedData(StrongSecret::new(value.expose().as_bytes().to_vec())), + ) + }) + .collect(); + Self { + data: DecryptedDataGroup(group), + identifier, + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct EncryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: DecryptedData, +} + +#[derive(Debug, Serialize, serde::Deserialize)] +pub struct DecryptedDataGroup(pub FxHashMap); + +#[derive(Debug, Serialize, Deserialize)] +pub struct BatchEncryptDataResponse { + pub data: EncryptedDataGroup, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct EncryptDataResponse { + pub data: EncryptedData, +} + +#[derive(Debug, Serialize, serde::Deserialize)] +pub struct EncryptedDataGroup(pub FxHashMap); +#[derive(Debug)] +pub struct TransientBatchDecryptDataRequest { + pub identifier: Identifier, + pub data: FxHashMap>>, +} + +#[derive(Debug)] +pub struct TransientDecryptDataRequest { + pub identifier: Identifier, + pub data: StrongSecret>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BatchDecryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: FxHashMap>, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DecryptDataRequest { + #[serde(flatten)] + pub identifier: Identifier, + pub data: StrongSecret, +} + +impl ForeignFrom<(FxHashMap>, BatchEncryptDataResponse)> + for FxHashMap>> +where + T: Clone, + S: Strategy + Send, +{ + fn foreign_from( + (mut masked_data, response): (FxHashMap>, BatchEncryptDataResponse), + ) -> Self { + response + .data + .0 + .into_iter() + .flat_map(|(k, v)| { + masked_data.remove(&k).map(|inner| { + ( + k, + Encryptable::new(inner.clone(), v.data.peek().clone().into()), + ) + }) + }) + .collect() + } +} + +impl ForeignFrom<(Secret, EncryptDataResponse)> for Encryptable> +where + T: Clone, + S: Strategy + Send, +{ + fn foreign_from((masked_data, response): (Secret, EncryptDataResponse)) -> Self { + Self::new(masked_data, response.data.data.peek().clone().into()) + } +} + +pub trait DecryptedDataConversion + Send>: Sized { + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult; +} + +impl + Send> DecryptedDataConversion + for Encryptable> +{ + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult { + let string = String::from_utf8(value.clone().inner().peek().clone()).map_err(|_err| { + #[cfg(feature = "encryption_service")] + logger::error!("Decryption error {:?}", _err); + errors::CryptoError::DecodingFailed + })?; + Ok(Self::new(Secret::new(string), encryption.into_inner())) + } +} + +impl + Send> DecryptedDataConversion + for Encryptable> +{ + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult { + let val = serde_json::from_slice(value.clone().inner().peek()).map_err(|_err| { + #[cfg(feature = "encryption_service")] + logger::error!("Decryption error {:?}", _err); + errors::CryptoError::DecodingFailed + })?; + Ok(Self::new(Secret::new(val), encryption.clone().into_inner())) + } +} + +impl> + Send> DecryptedDataConversion, S> + for Encryptable, S>> +{ + fn convert( + value: &DecryptedData, + encryption: Encryption, + ) -> CustomResult { + Ok(Self::new( + Secret::new(value.clone().inner().peek().clone()), + encryption.clone().into_inner(), + )) + } +} + +impl ForeignTryFrom<(Encryption, DecryptDataResponse)> for Encryptable> +where + T: Clone, + S: Strategy + Send, + Self: DecryptedDataConversion, +{ + type Error = error_stack::Report; + fn foreign_try_from( + (encrypted_data, response): (Encryption, DecryptDataResponse), + ) -> Result { + Self::convert(&response.data, encrypted_data) + } +} + +impl ForeignTryFrom<(FxHashMap, BatchDecryptDataResponse)> + for FxHashMap>> +where + T: Clone, + S: Strategy + Send, + Encryptable>: DecryptedDataConversion, +{ + type Error = error_stack::Report; + fn foreign_try_from( + (mut encrypted_data, response): (FxHashMap, BatchDecryptDataResponse), + ) -> Result { + response + .data + .0 + .into_iter() + .map(|(k, v)| match encrypted_data.remove(&k) { + Some(encrypted) => Ok((k.clone(), Encryptable::convert(&v, encrypted.clone())?)), + None => Err(errors::CryptoError::DecodingFailed)?, + }) + .collect() + } +} + +impl From<(Encryption, Identifier)> for TransientDecryptDataRequest { + fn from((encryption, identifier): (Encryption, Identifier)) -> Self { + Self { + data: StrongSecret::new(encryption.clone().into_inner().expose()), + identifier, + } + } +} + +impl From<(FxHashMap, Identifier)> for TransientBatchDecryptDataRequest { + fn from((map, identifier): (FxHashMap, Identifier)) -> Self { + let data = map + .into_iter() + .map(|(k, v)| (k, StrongSecret::new(v.clone().into_inner().expose()))) + .collect(); + Self { data, identifier } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct BatchDecryptDataResponse { + pub data: DecryptedDataGroup, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DecryptDataResponse { + pub data: DecryptedData, +} + +#[derive(Clone, Debug)] +pub struct DecryptedData(StrongSecret>); + +impl Serialize for DecryptedData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let data = BASE64_ENGINE.encode(self.0.peek()); + serializer.serialize_str(&data) + } +} + +impl<'de> Deserialize<'de> for DecryptedData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct DecryptedDataVisitor; + + impl<'de> Visitor<'de> for DecryptedDataVisitor { + type Value = DecryptedData; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("string of the format {version}:{base64_encoded_data}'") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let dec_data = BASE64_ENGINE.decode(value).map_err(|err| { + let err = err.to_string(); + E::invalid_value(Unexpected::Str(value), &err.as_str()) + })?; + + Ok(DecryptedData(dec_data.into())) + } + } + + deserializer.deserialize_str(DecryptedDataVisitor) + } +} + +impl DecryptedData { + pub fn from_data(data: StrongSecret>) -> Self { + Self(data) + } + + pub fn inner(self) -> StrongSecret> { + self.0 + } +} + +#[derive(Debug)] +pub struct EncryptedData { + pub data: StrongSecret>, +} + +impl Serialize for EncryptedData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let data = String::from_utf8(self.data.peek().clone()).map_err(ser::Error::custom)?; + serializer.serialize_str(data.as_str()) + } +} + +impl<'de> Deserialize<'de> for EncryptedData { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct EncryptedDataVisitor; + + impl<'de> Visitor<'de> for EncryptedDataVisitor { + type Value = EncryptedData; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("string of the format {version}:{base64_encoded_data}'") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Ok(EncryptedData { + data: StrongSecret::new(value.as_bytes().to_vec()), + }) + } + } + + deserializer.deserialize_str(EncryptedDataVisitor) + } +} + +/// A trait which converts the struct to Hashmap required for encryption and back to struct +pub trait ToEncryptable: Sized { + /// Serializes the type to a hashmap + fn to_encryptable(self) -> FxHashMap; + /// Deserializes the hashmap back to the type + fn from_encryptable( + hashmap: FxHashMap>, + ) -> CustomResult; +} diff --git a/crates/diesel_models/Cargo.toml b/crates/diesel_models/Cargo.toml index f33427aaf309..10890ef0cf0e 100644 --- a/crates/diesel_models/Cargo.toml +++ b/crates/diesel_models/Cargo.toml @@ -15,6 +15,7 @@ kv_store = [] async-bb8-diesel = { git = "https://github.com/jarnura/async-bb8-diesel", rev = "53b4ab901aab7635c8215fd1c2d542c8db443094" } diesel = { version = "2.1.5", features = ["postgres", "serde_json", "time", "64-column-tables"] } error-stack = "0.4.1" +rustc-hash = "1.1.0" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" strum = { version = "0.26.2", features = ["derive"] } diff --git a/crates/diesel_models/src/address.rs b/crates/diesel_models/src/address.rs index d695d2a0e707..57c4e36c786b 100644 --- a/crates/diesel_models/src/address.rs +++ b/crates/diesel_models/src/address.rs @@ -1,9 +1,17 @@ -use common_utils::id_type; +use common_utils::{ + crypto::{self, Encryptable}, + encryption::Encryption, + id_type, + pii::EmailStrategy, + types::keymanager::ToEncryptable, +}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; +use masking::{Secret, SwitchStrategy}; +use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums, schema::address}; +use crate::{enums, schema::address}; #[derive(Clone, Debug, Insertable, Serialize, Deserialize, router_derive::DebugAsDisplay)] #[diesel(table_name = address)] @@ -54,6 +62,62 @@ pub struct Address { pub email: Option, } +#[derive(Clone)] +// Intermediate struct to convert HashMap to Address +pub struct EncryptableAddress { + pub line1: crypto::OptionalEncryptableSecretString, + pub line2: crypto::OptionalEncryptableSecretString, + pub line3: crypto::OptionalEncryptableSecretString, + pub state: crypto::OptionalEncryptableSecretString, + pub zip: crypto::OptionalEncryptableSecretString, + pub first_name: crypto::OptionalEncryptableSecretString, + pub last_name: crypto::OptionalEncryptableSecretString, + pub phone_number: crypto::OptionalEncryptableSecretString, + pub email: crypto::OptionalEncryptableEmail, +} + +impl ToEncryptable, Encryption> for Address { + fn to_encryptable(self) -> FxHashMap { + let mut map = FxHashMap::with_capacity_and_hasher(9, Default::default()); + self.line1.map(|x| map.insert("line1".to_string(), x)); + self.line2.map(|x| map.insert("line2".to_string(), x)); + self.line3.map(|x| map.insert("line3".to_string(), x)); + self.zip.map(|x| map.insert("zip".to_string(), x)); + self.state.map(|x| map.insert("state".to_string(), x)); + self.first_name + .map(|x| map.insert("first_name".to_string(), x)); + self.last_name + .map(|x| map.insert("last_name".to_string(), x)); + self.phone_number + .map(|x| map.insert("phone_number".to_string(), x)); + self.email.map(|x| map.insert("email".to_string(), x)); + map + } + + fn from_encryptable( + mut hashmap: FxHashMap>>, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableAddress { + line1: hashmap.remove("line1"), + line2: hashmap.remove("line2"), + line3: hashmap.remove("line3"), + zip: hashmap.remove("zip"), + state: hashmap.remove("state"), + first_name: hashmap.remove("first_name"), + last_name: hashmap.remove("last_name"), + phone_number: hashmap.remove("phone_number"), + email: hashmap.remove("email").map(|email| { + let encryptable: Encryptable> = Encryptable::new( + email.clone().into_inner().switch_strategy(), + email.into_encrypted(), + ); + encryptable + }), + }) + } +} + #[derive(Clone, Debug, AsChangeset, router_derive::DebugAsDisplay, Serialize, Deserialize)] #[diesel(table_name = address)] pub struct AddressUpdateInternal { diff --git a/crates/diesel_models/src/business_profile.rs b/crates/diesel_models/src/business_profile.rs index 6bd1e1f9e41a..b964b290a9ec 100644 --- a/crates/diesel_models/src/business_profile.rs +++ b/crates/diesel_models/src/business_profile.rs @@ -1,7 +1,7 @@ -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; -use crate::{encryption::Encryption, schema::business_profile}; +use crate::schema::business_profile; #[derive( Clone, diff --git a/crates/diesel_models/src/customers.rs b/crates/diesel_models/src/customers.rs index deb1ac73f41d..302772b7f9e4 100644 --- a/crates/diesel_models/src/customers.rs +++ b/crates/diesel_models/src/customers.rs @@ -1,8 +1,8 @@ -use common_utils::{id_type, pii}; +use common_utils::{encryption::Encryption, id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, schema::customers}; +use crate::schema::customers; #[derive( Clone, Debug, Insertable, router_derive::DebugAsDisplay, serde::Deserialize, serde::Serialize, diff --git a/crates/diesel_models/src/events.rs b/crates/diesel_models/src/events.rs index b3b1230441f4..99879dafaf93 100644 --- a/crates/diesel_models/src/events.rs +++ b/crates/diesel_models/src/events.rs @@ -1,11 +1,15 @@ -use common_utils::custom_serde; +use common_utils::{ + crypto::OptionalEncryptableSecretString, custom_serde, encryption::Encryption, + types::keymanager::ToEncryptable, +}; use diesel::{ expression::AsExpression, AsChangeset, Identifiable, Insertable, Queryable, Selectable, }; +use masking::Secret; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums as storage_enums, schema::events}; +use crate::{enums as storage_enums, schema::events}; #[derive(Clone, Debug, Insertable, router_derive::DebugAsDisplay)] #[diesel(table_name = events)] @@ -59,6 +63,38 @@ pub struct Event { pub metadata: Option, } +pub struct EventWithEncryption { + pub request: Option, + pub response: Option, +} + +pub struct EncryptableEvent { + pub request: OptionalEncryptableSecretString, + pub response: OptionalEncryptableSecretString, +} + +impl ToEncryptable, Encryption> for EventWithEncryption { + fn to_encryptable(self) -> rustc_hash::FxHashMap { + let mut map = rustc_hash::FxHashMap::default(); + self.request.map(|x| map.insert("request".to_string(), x)); + self.response.map(|x| map.insert("response".to_string(), x)); + map + } + + fn from_encryptable( + mut hashmap: rustc_hash::FxHashMap< + String, + common_utils::crypto::Encryptable>, + >, + ) -> common_utils::errors::CustomResult + { + Ok(EncryptableEvent { + request: hashmap.remove("request"), + response: hashmap.remove("response"), + }) + } +} + #[derive(Clone, Debug, Deserialize, Serialize, AsExpression, diesel::FromSqlRow)] #[diesel(sql_type = diesel::sql_types::Jsonb)] pub enum EventMetadata { diff --git a/crates/diesel_models/src/lib.rs b/crates/diesel_models/src/lib.rs index 67061bf6c336..6af1e0e52084 100644 --- a/crates/diesel_models/src/lib.rs +++ b/crates/diesel_models/src/lib.rs @@ -12,7 +12,6 @@ pub mod blocklist; pub mod blocklist_fingerprint; pub mod customers; pub mod dispute; -pub mod encryption; pub mod enums; pub mod ephemeral_key; pub mod errors; diff --git a/crates/diesel_models/src/merchant_account.rs b/crates/diesel_models/src/merchant_account.rs index 71c437bbb418..5c48ade55c02 100644 --- a/crates/diesel_models/src/merchant_account.rs +++ b/crates/diesel_models/src/merchant_account.rs @@ -1,7 +1,7 @@ -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; -use crate::{encryption::Encryption, enums as storage_enums, schema::merchant_account}; +use crate::{enums as storage_enums, schema::merchant_account}; #[derive( Clone, diff --git a/crates/diesel_models/src/merchant_connector_account.rs b/crates/diesel_models/src/merchant_connector_account.rs index 05707f71737a..5a549892dca5 100644 --- a/crates/diesel_models/src/merchant_connector_account.rs +++ b/crates/diesel_models/src/merchant_connector_account.rs @@ -1,10 +1,10 @@ use std::fmt::Debug; -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; -use crate::{encryption::Encryption, enums as storage_enums, schema::merchant_connector_account}; +use crate::{enums as storage_enums, schema::merchant_connector_account}; #[derive( Clone, diff --git a/crates/diesel_models/src/merchant_key_store.rs b/crates/diesel_models/src/merchant_key_store.rs index 30781927e94f..b11d99705323 100644 --- a/crates/diesel_models/src/merchant_key_store.rs +++ b/crates/diesel_models/src/merchant_key_store.rs @@ -1,8 +1,8 @@ -use common_utils::custom_serde; +use common_utils::{custom_serde, encryption::Encryption}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, schema::merchant_key_store}; +use crate::schema::merchant_key_store; #[derive( Clone, diff --git a/crates/diesel_models/src/payment_intent.rs b/crates/diesel_models/src/payment_intent.rs index 499d72a648a8..e4d8dcc315a7 100644 --- a/crates/diesel_models/src/payment_intent.rs +++ b/crates/diesel_models/src/payment_intent.rs @@ -1,10 +1,10 @@ use common_enums::RequestIncrementalAuthorization; -use common_utils::{id_type, pii, types::MinorUnit}; +use common_utils::{encryption::Encryption, id_type, pii, types::MinorUnit}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums as storage_enums, schema::payment_intent}; +use crate::{enums as storage_enums, schema::payment_intent}; #[derive(Clone, Debug, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize)] #[diesel(table_name = payment_intent, primary_key(payment_id, merchant_id), check_for_backend(diesel::pg::Pg))] diff --git a/crates/diesel_models/src/payment_method.rs b/crates/diesel_models/src/payment_method.rs index a9db90abb20d..6bb40e4b1666 100644 --- a/crates/diesel_models/src/payment_method.rs +++ b/crates/diesel_models/src/payment_method.rs @@ -1,11 +1,11 @@ use common_enums::MerchantStorageScheme; -use common_utils::{id_type, pii}; +use common_utils::{encryption::Encryption, id_type, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; use serde::{Deserialize, Serialize}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums as storage_enums, schema::payment_methods}; +use crate::{enums as storage_enums, schema::payment_methods}; #[derive( Clone, Debug, Eq, PartialEq, Identifiable, Queryable, Selectable, Serialize, Deserialize, diff --git a/crates/diesel_models/src/user.rs b/crates/diesel_models/src/user.rs index 56c6f00f2b04..9d6514916d86 100644 --- a/crates/diesel_models/src/user.rs +++ b/crates/diesel_models/src/user.rs @@ -1,11 +1,9 @@ -use common_utils::pii; +use common_utils::{encryption::Encryption, pii}; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use masking::Secret; use time::PrimitiveDateTime; -use crate::{ - diesel_impl::OptionalDieselArray, encryption::Encryption, enums::TotpStatus, schema::users, -}; +use crate::{diesel_impl::OptionalDieselArray, enums::TotpStatus, schema::users}; pub mod dashboard_metadata; diff --git a/crates/diesel_models/src/user_authentication_method.rs b/crates/diesel_models/src/user_authentication_method.rs index 18a65b9f104d..76e1abe75755 100644 --- a/crates/diesel_models/src/user_authentication_method.rs +++ b/crates/diesel_models/src/user_authentication_method.rs @@ -1,7 +1,8 @@ +use common_utils::encryption::Encryption; use diesel::{AsChangeset, Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, enums, schema::user_authentication_methods}; +use crate::{enums, schema::user_authentication_methods}; #[derive(Clone, Debug, Identifiable, Queryable, Selectable)] #[diesel(table_name = user_authentication_methods, check_for_backend(diesel::pg::Pg))] diff --git a/crates/diesel_models/src/user_key_store.rs b/crates/diesel_models/src/user_key_store.rs index 1b19ef31a491..4d880ebf8f95 100644 --- a/crates/diesel_models/src/user_key_store.rs +++ b/crates/diesel_models/src/user_key_store.rs @@ -1,7 +1,8 @@ +use common_utils::encryption::Encryption; use diesel::{Identifiable, Insertable, Queryable, Selectable}; use time::PrimitiveDateTime; -use crate::{encryption::Encryption, schema::user_key_store}; +use crate::schema::user_key_store; #[derive( Clone, Debug, serde::Serialize, serde::Deserialize, Identifiable, Queryable, Selectable, diff --git a/crates/hyperswitch_domain_models/Cargo.toml b/crates/hyperswitch_domain_models/Cargo.toml index ab5694646842..310c2731b42e 100644 --- a/crates/hyperswitch_domain_models/Cargo.toml +++ b/crates/hyperswitch_domain_models/Cargo.toml @@ -9,6 +9,7 @@ license.workspace = true [features] default = ["olap", "payouts", "frm"] +encryption_service =[] olap = [] payouts = ["api_models/payouts"] frm = ["api_models/frm"] @@ -18,7 +19,7 @@ frm = ["api_models/frm"] api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } -common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext", "metrics"] } +common_utils = { version = "0.1.0", path = "../common_utils", features = ["async_ext", "metrics", "encryption_service", "keymanager"] } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } masking = { version = "0.1.0", path = "../masking" } router_derive = { version = "0.1.0", path = "../router_derive" } @@ -31,6 +32,7 @@ error-stack = "0.4.1" futures = "0.3.30" http = "0.2.12" mime = "0.3.17" +rustc-hash = "1.1.0" serde = { version = "1.0.197", features = ["derive"] } serde_json = "1.0.115" serde_with = "3.7.0" diff --git a/crates/hyperswitch_domain_models/src/behaviour.rs b/crates/hyperswitch_domain_models/src/behaviour.rs index 9976dd25a985..07911e8ea693 100644 --- a/crates/hyperswitch_domain_models/src/behaviour.rs +++ b/crates/hyperswitch_domain_models/src/behaviour.rs @@ -1,4 +1,7 @@ -use common_utils::errors::{CustomResult, ValidationError}; +use common_utils::{ + errors::{CustomResult, ValidationError}, + types::keymanager::KeyManagerState, +}; use masking::Secret; /// Trait for converting domain types to storage models @@ -9,8 +12,10 @@ pub trait Conversion { async fn convert(self) -> CustomResult; async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized; @@ -20,12 +25,22 @@ pub trait Conversion { #[async_trait::async_trait] pub trait ReverseConversion { - async fn convert(self, key: &Secret>) -> CustomResult; + async fn convert( + self, + state: &KeyManagerState, + key: &Secret>, + key_store_ref_id: String, + ) -> CustomResult; } #[async_trait::async_trait] impl> ReverseConversion for T { - async fn convert(self, key: &Secret>) -> CustomResult { - U::convert_back(self, key).await + async fn convert( + self, + state: &KeyManagerState, + key: &Secret>, + key_store_ref_id: String, + ) -> CustomResult { + U::convert_back(state, self, key, key_store_ref_id).await } } diff --git a/crates/hyperswitch_domain_models/src/merchant_account.rs b/crates/hyperswitch_domain_models/src/merchant_account.rs index e42f21dc7c78..a430673c59a3 100644 --- a/crates/hyperswitch_domain_models/src/merchant_account.rs +++ b/crates/hyperswitch_domain_models/src/merchant_account.rs @@ -1,13 +1,14 @@ use common_utils::{ crypto::{OptionalEncryptableName, OptionalEncryptableValue}, date_time, + encryption::Encryption, errors::{CustomResult, ValidationError}, ext_traits::ValueExt, pii, + types::keymanager, }; use diesel_models::{ - encryption::Encryption, enums::MerchantStorageScheme, - merchant_account::MerchantAccountUpdateInternal, + enums::MerchantStorageScheme, merchant_account::MerchantAccountUpdateInternal, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -195,8 +196,10 @@ impl super::behaviour::Conversion for MerchantAccount { } async fn convert_back( + state: &keymanager::KeyManagerState, item: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized, @@ -206,7 +209,7 @@ impl super::behaviour::Conversion for MerchantAccount { .ok_or(ValidationError::MissingRequiredField { field_name: "publishable_key".to_string(), })?; - + let identifier = keymanager::Identifier::Merchant(key_store_ref_id.clone()); async { Ok::>(Self { id: Some(item.id), @@ -217,11 +220,11 @@ impl super::behaviour::Conversion for MerchantAccount { redirect_to_merchant_with_http_post: item.redirect_to_merchant_with_http_post, merchant_name: item .merchant_name - .async_lift(|inner| decrypt(inner, key.peek())) + .async_lift(|inner| decrypt(state, inner, identifier.clone(), key.peek())) .await?, merchant_details: item .merchant_details - .async_lift(|inner| decrypt(inner, key.peek())) + .async_lift(|inner| decrypt(state, inner, identifier.clone(), key.peek())) .await?, webhook_details: item.webhook_details, sub_merchants_enabled: item.sub_merchants_enabled, diff --git a/crates/hyperswitch_domain_models/src/merchant_key_store.rs b/crates/hyperswitch_domain_models/src/merchant_key_store.rs index 29050b7eb496..76acf0f14045 100644 --- a/crates/hyperswitch_domain_models/src/merchant_key_store.rs +++ b/crates/hyperswitch_domain_models/src/merchant_key_store.rs @@ -2,6 +2,7 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, custom_serde, date_time, errors::{CustomResult, ValidationError}, + types::keymanager::{Identifier, KeyManagerState}, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -30,14 +31,17 @@ impl super::behaviour::Conversion for MerchantKeyStore { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult where Self: Sized, { + let identifier = Identifier::Merchant(item.merchant_id.clone()); Ok(Self { - key: Encryptable::decrypt(item.key, key.peek(), GcmAes256) + key: Encryptable::decrypt_via_api(state, item.key, identifier, key.peek(), GcmAes256) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting customer data".to_string(), diff --git a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs index 6ff74a95e46c..5f8dd3934a2b 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_attempt.rs @@ -1,9 +1,10 @@ use api_models::enums::Connector; use common_enums as storage_enums; use common_utils::{ + encryption::Encryption, errors::{CustomResult, ValidationError}, pii, - types::MinorUnit, + types::{keymanager::KeyManagerState, MinorUnit}, }; use error_stack::ResultExt; use masking::PeekInterface; @@ -479,8 +480,7 @@ impl ForeignIDRef for PaymentAttempt { } use diesel_models::{ - encryption::Encryption, PaymentIntent as DieselPaymentIntent, - PaymentIntentNew as DieselPaymentIntentNew, + PaymentIntent as DieselPaymentIntent, PaymentIntentNew as DieselPaymentIntentNew, }; #[async_trait::async_trait] @@ -542,14 +542,23 @@ impl behaviour::Conversion for PaymentIntent { } async fn convert_back( + state: &KeyManagerState, storage_model: Self::DstType, key: &masking::Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized, { async { - let inner_decrypt = |inner| decrypt(inner, key.peek()); + let inner_decrypt = |inner| { + decrypt( + state, + inner, + common_utils::types::keymanager::Identifier::Merchant(key_store_ref_id.clone()), + key.peek(), + ) + }; Ok::>(Self { payment_id: storage_model.payment_id, merchant_id: storage_model.merchant_id, diff --git a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs index 6eb356167292..f4f6be970555 100644 --- a/crates/hyperswitch_domain_models/src/payments/payment_intent.rs +++ b/crates/hyperswitch_domain_models/src/payments/payment_intent.rs @@ -2,9 +2,10 @@ use common_enums as storage_enums; use common_utils::{ consts::{PAYMENTS_LIST_MAX_LIMIT_V1, PAYMENTS_LIST_MAX_LIMIT_V2}, crypto::Encryptable, + encryption::Encryption, id_type, pii::{self, Email}, - types::MinorUnit, + types::{keymanager::KeyManagerState, MinorUnit}, }; use masking::{Deserialize, Secret}; use serde::Serialize; @@ -16,6 +17,7 @@ use crate::{errors, merchant_key_store::MerchantKeyStore, RemoteStorageObject}; pub trait PaymentIntentInterface { async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, payment_intent: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, @@ -24,6 +26,7 @@ pub trait PaymentIntentInterface { async fn insert_payment_intent( &self, + state: &KeyManagerState, new: PaymentIntent, merchant_key_store: &MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, @@ -31,6 +34,7 @@ pub trait PaymentIntentInterface { async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, merchant_key_store: &MerchantKeyStore, @@ -46,6 +50,7 @@ pub trait PaymentIntentInterface { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -55,6 +60,7 @@ pub trait PaymentIntentInterface { #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, merchant_key_store: &MerchantKeyStore, @@ -64,6 +70,7 @@ pub trait PaymentIntentInterface { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, constraints: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -467,7 +474,7 @@ impl From for PaymentIntentUpdateInternal { } use diesel_models::{ - encryption::Encryption, PaymentIntentUpdate as DieselPaymentIntentUpdate, + PaymentIntentUpdate as DieselPaymentIntentUpdate, PaymentIntentUpdateFields as DieselPaymentIntentUpdateFields, }; diff --git a/crates/hyperswitch_domain_models/src/type_encryption.rs b/crates/hyperswitch_domain_models/src/type_encryption.rs index 82d904ccf49f..d656afc92512 100644 --- a/crates/hyperswitch_domain_models/src/type_encryption.rs +++ b/crates/hyperswitch_domain_models/src/type_encryption.rs @@ -1,14 +1,30 @@ use async_trait::async_trait; use common_utils::{ crypto, + encryption::Encryption, errors::{self, CustomResult}, ext_traits::AsyncExt, metrics::utils::record_operation_time, + types::keymanager::{Identifier, KeyManagerState}, }; -use diesel_models::encryption::Encryption; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use router_env::{instrument, tracing}; +use rustc_hash::FxHashMap; +#[cfg(feature = "encryption_service")] +use { + common_utils::{ + keymanager::call_encryption_service, + transformers::{ForeignFrom, ForeignTryFrom}, + types::keymanager::{ + BatchDecryptDataResponse, BatchEncryptDataRequest, BatchEncryptDataResponse, + DecryptDataResponse, EncryptDataRequest, EncryptDataResponse, + TransientBatchDecryptDataRequest, TransientDecryptDataRequest, + }, + }, + http::Method, + router_env::logger, +}; #[async_trait] pub trait TypeEncryption< @@ -17,6 +33,22 @@ pub trait TypeEncryption< S: masking::Strategy, >: Sized { + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult; + + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult; + async fn encrypt( masked_data: Secret, key: &[u8], @@ -28,31 +60,141 @@ pub trait TypeEncryption< key: &[u8], crypt_algo: V, ) -> CustomResult; + + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; + + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; + + async fn batch_encrypt( + masked_data: FxHashMap>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError>; } #[async_trait] impl< V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy + Send, + S: masking::Strategy + Send + Sync, > TypeEncryption for crypto::Encryptable> { #[instrument(skip_all)] + #[allow(unused_variables)] + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + EncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + EncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data.clone(), response))), + Err(err) => { + logger::error!("Encryption error {:?}", err); + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Encryption"); + Self::encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + DecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + async fn encrypt( masked_data: Secret, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted_data = crypt_algo.encode_message(key, masked_data.peek().as_bytes())?; - Ok(Self::new(masked_data, encrypted_data.into())) } - #[instrument(skip_all)] async fn decrypt( encrypted_data: Encryption, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted = encrypted_data.into_inner(); let data = crypt_algo.decode_message(key, encrypted.clone())?; @@ -62,25 +204,227 @@ impl< Ok(Self::new(value.into(), encrypted)) } + + #[allow(unused_variables)] + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchEncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + BatchEncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data, response))), + Err(err) => { + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::error!("Encryption error {:?}", err); + logger::info!("Fall back to Application Encryption"); + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[allow(unused_variables)] + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchDecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientBatchDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + + async fn batch_encrypt( + masked_data: FxHashMap>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + masked_data + .into_iter() + .map(|(k, v)| { + Ok(( + k, + Self::new( + v.clone(), + crypt_algo.encode_message(key, v.peek().as_bytes())?.into(), + ), + )) + }) + .collect() + } + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + encrypted_data + .into_iter() + .map(|(k, v)| { + let data = crypt_algo.decode_message(key, v.clone().into_inner())?; + let value: String = std::str::from_utf8(&data) + .change_context(errors::CryptoError::DecodingFailed)? + .to_string(); + Ok((k, Self::new(value.into(), v.into_inner()))) + }) + .collect() + } } #[async_trait] impl< V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy + Send, + S: masking::Strategy + Send + Sync, > TypeEncryption for crypto::Encryptable> { + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + EncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + EncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data.clone(), response))), + Err(err) => { + logger::error!("Encryption error {:?}", err); + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Encryption"); + Self::encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + DecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::EncodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + #[instrument(skip_all)] async fn encrypt( masked_data: Secret, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let data = serde_json::to_vec(&masked_data.peek()) .change_context(errors::CryptoError::DecodingFailed)?; let encrypted_data = crypt_algo.encode_message(key, &data)?; - Ok(Self::new(masked_data, encrypted_data.into())) } @@ -90,30 +434,229 @@ impl< key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted = encrypted_data.into_inner(); let data = crypt_algo.decode_message(key, encrypted.clone())?; let value: serde_json::Value = serde_json::from_slice(&data).change_context(errors::CryptoError::DecodingFailed)?; - Ok(Self::new(value.into(), encrypted)) } + + #[allow(unused_variables)] + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchEncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + BatchEncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data, response))), + Err(err) => { + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::error!("Encryption error {:?}", err); + logger::info!("Fall back to Application Encryption"); + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[allow(unused_variables)] + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchDecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientBatchDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + + async fn batch_encrypt( + masked_data: FxHashMap>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + masked_data + .into_iter() + .map(|(k, v)| { + let data = serde_json::to_vec(v.peek()) + .change_context(errors::CryptoError::DecodingFailed)?; + Ok(( + k, + Self::new(v, crypt_algo.encode_message(key, &data)?.into()), + )) + }) + .collect() + } + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + encrypted_data + .into_iter() + .map(|(k, v)| { + let data = crypt_algo.decode_message(key, v.clone().into_inner().clone())?; + + let value: serde_json::Value = serde_json::from_slice(&data) + .change_context(errors::CryptoError::DecodingFailed)?; + Ok((k, Self::new(value.into(), v.into_inner()))) + }) + .collect() + } } #[async_trait] impl< V: crypto::DecodeMessage + crypto::EncodeMessage + Send + 'static, - S: masking::Strategy> + Send, + S: masking::Strategy> + Send + Sync, > TypeEncryption, V, S> for crypto::Encryptable, S>> { + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn encrypt_via_api( + state: &KeyManagerState, + masked_data: Secret, S>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::encrypt(masked_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + EncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + EncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data.clone(), response))), + Err(err) => { + logger::error!("Encryption error {:?}", err); + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Encryption"); + Self::encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[instrument(skip_all)] + #[allow(unused_variables)] + async fn decrypt_via_api( + state: &KeyManagerState, + encrypted_data: Encryption, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult { + #[cfg(not(feature = "encryption_service"))] + { + Self::decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + DecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(decrypted_data) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), decrypted_data)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + #[instrument(skip_all)] async fn encrypt( masked_data: Secret, S>, key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted_data = crypt_algo.encode_message(key, masked_data.peek())?; - Ok(Self::new(masked_data, encrypted_data.into())) } @@ -123,11 +666,131 @@ impl< key: &[u8], crypt_algo: V, ) -> CustomResult { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); let encrypted = encrypted_data.into_inner(); let data = crypt_algo.decode_message(key, encrypted.clone())?; - Ok(Self::new(data.into(), encrypted)) } + + #[allow(unused_variables)] + async fn batch_encrypt_via_api( + state: &KeyManagerState, + masked_data: FxHashMap, S>>, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchEncryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/encrypt", + BatchEncryptDataRequest::from((masked_data.clone(), identifier)), + ) + .await; + match result { + Ok(response) => Ok(ForeignFrom::foreign_from((masked_data, response))), + Err(err) => { + metrics::ENCRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::error!("Encryption error {:?}", err); + logger::info!("Fall back to Application Encryption"); + Self::batch_encrypt(masked_data, key, crypt_algo).await + } + } + } + } + + #[allow(unused_variables)] + async fn batch_decrypt_via_api( + state: &KeyManagerState, + encrypted_data: FxHashMap, + identifier: Identifier, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + #[cfg(not(feature = "encryption_service"))] + { + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + #[cfg(feature = "encryption_service")] + { + let result: Result< + BatchDecryptDataResponse, + error_stack::Report, + > = call_encryption_service( + state, + Method::POST, + "data/decrypt", + TransientBatchDecryptDataRequest::from((encrypted_data.clone(), identifier)), + ) + .await; + let decrypted = match result { + Ok(response) => { + ForeignTryFrom::foreign_try_from((encrypted_data.clone(), response)) + } + Err(err) => { + logger::error!("Decryption error {:?}", err); + Err(err.change_context(errors::CryptoError::DecodingFailed)) + } + }; + match decrypted { + Ok(de) => Ok(de), + Err(_) => { + metrics::DECRYPTION_API_FAILURES.add(&metrics::CONTEXT, 1, &[]); + logger::info!("Fall back to Application Decryption"); + Self::batch_decrypt(encrypted_data, key, crypt_algo).await + } + } + } + } + + async fn batch_encrypt( + masked_data: FxHashMap, S>>, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_ENCRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + masked_data + .into_iter() + .map(|(k, v)| { + Ok(( + k, + Self::new(v.clone(), crypt_algo.encode_message(key, v.peek())?.into()), + )) + }) + .collect() + } + + async fn batch_decrypt( + encrypted_data: FxHashMap, + key: &[u8], + crypt_algo: V, + ) -> CustomResult, errors::CryptoError> { + metrics::APPLICATION_DECRYPTION_COUNT.add(&metrics::CONTEXT, 1, &[]); + encrypted_data + .into_iter() + .map(|(k, v)| { + Ok(( + k, + Self::new( + crypt_algo + .decode_message(key, v.clone().into_inner().clone())? + .into(), + v.into_inner(), + ), + )) + }) + .collect() + } } pub trait Lift { @@ -178,7 +841,9 @@ impl + Lift = V> + Send> AsyncLift for V { #[inline] pub async fn encrypt( + state: &KeyManagerState, inner: Secret, + identifier: Identifier, key: &[u8], ) -> CustomResult>, errors::CryptoError> where @@ -186,7 +851,7 @@ where crypto::Encryptable>: TypeEncryption, { record_operation_time( - crypto::Encryptable::encrypt(inner, key, crypto::GcmAes256), + crypto::Encryptable::encrypt_via_api(state, inner, identifier, key, crypto::GcmAes256), &metrics::ENCRYPTION_TIME, &metrics::CONTEXT, &[], @@ -194,9 +859,41 @@ where .await } +#[inline] +pub async fn batch_encrypt( + state: &KeyManagerState, + inner: FxHashMap>, + identifier: Identifier, + key: &[u8], +) -> CustomResult>>, errors::CryptoError> +where + S: masking::Strategy, + crypto::Encryptable>: TypeEncryption, +{ + if !inner.is_empty() { + record_operation_time( + crypto::Encryptable::batch_encrypt_via_api( + state, + inner, + identifier, + key, + crypto::GcmAes256, + ), + &metrics::ENCRYPTION_TIME, + &metrics::CONTEXT, + &[], + ) + .await + } else { + Ok(FxHashMap::default()) + } +} + #[inline] pub async fn encrypt_optional( + state: &KeyManagerState, inner: Option>, + identifier: Identifier, key: &[u8], ) -> CustomResult>>, errors::CryptoError> where @@ -204,19 +901,26 @@ where S: masking::Strategy, crypto::Encryptable>: TypeEncryption, { - inner.async_map(|f| encrypt(f, key)).await.transpose() + inner + .async_map(|f| encrypt(state, f, identifier, key)) + .await + .transpose() } #[inline] pub async fn decrypt>( + state: &KeyManagerState, inner: Option, + identifier: Identifier, key: &[u8], ) -> CustomResult>>, errors::CryptoError> where crypto::Encryptable>: TypeEncryption, { record_operation_time( - inner.async_map(|item| crypto::Encryptable::decrypt(item, key, crypto::GcmAes256)), + inner.async_map(|item| { + crypto::Encryptable::decrypt_via_api(state, item, identifier, key, crypto::GcmAes256) + }), &metrics::DECRYPTION_TIME, &metrics::CONTEXT, &[], @@ -225,8 +929,38 @@ where .transpose() } +#[inline] +pub async fn batch_decrypt( + state: &KeyManagerState, + inner: FxHashMap, + identifier: Identifier, + key: &[u8], +) -> CustomResult>>, errors::CryptoError> +where + S: masking::Strategy, + crypto::Encryptable>: TypeEncryption, +{ + if !inner.is_empty() { + record_operation_time( + crypto::Encryptable::batch_decrypt_via_api( + state, + inner, + identifier, + key, + crypto::GcmAes256, + ), + &metrics::ENCRYPTION_TIME, + &metrics::CONTEXT, + &[], + ) + .await + } else { + Ok(FxHashMap::default()) + } +} + pub(crate) mod metrics { - use router_env::{global_meter, histogram_metric, metrics_context, once_cell}; + use router_env::{counter_metric, global_meter, histogram_metric, metrics_context, once_cell}; metrics_context!(CONTEXT); global_meter!(GLOBAL_METER, "ROUTER_API"); @@ -234,4 +968,8 @@ pub(crate) mod metrics { // Encryption and Decryption metrics histogram_metric!(ENCRYPTION_TIME, GLOBAL_METER); histogram_metric!(DECRYPTION_TIME, GLOBAL_METER); + counter_metric!(ENCRYPTION_API_FAILURES, GLOBAL_METER); + counter_metric!(DECRYPTION_API_FAILURES, GLOBAL_METER); + counter_metric!(APPLICATION_ENCRYPTION_COUNT, GLOBAL_METER); + counter_metric!(APPLICATION_DECRYPTION_COUNT, GLOBAL_METER); } diff --git a/crates/router/Cargo.toml b/crates/router/Cargo.toml index 1379b17eec44..b9488a0804ed 100644 --- a/crates/router/Cargo.toml +++ b/crates/router/Cargo.toml @@ -11,12 +11,13 @@ license.workspace = true [features] default = ["kv_store", "stripe", "oltp", "olap", "accounts_cache", "dummy_connector", "payouts", "payout_retry", "retry", "frm", "tls"] tls = ["actix-web/rustls-0_22"] -keymanager_mtls = ["reqwest/rustls-tls", "common_utils/keymanager_mtls"] +keymanager_mtls = ["reqwest/rustls-tls","common_utils/keymanager_mtls"] +encryption_service = ["hyperswitch_domain_models/encryption_service", "common_utils/encryption_service"] email = ["external_services/email", "scheduler/email", "olap"] keymanager_create = [] frm = ["api_models/frm", "hyperswitch_domain_models/frm"] stripe = ["dep:serde_qs"] -release = ["stripe", "email", "accounts_cache", "kv_store", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3", "keymanager_mtls", "keymanager_create"] +release = ["stripe", "email", "accounts_cache", "kv_store", "vergen", "recon", "external_services/aws_kms", "external_services/aws_s3", "keymanager_mtls", "keymanager_create", "encryption_service"] olap = ["hyperswitch_domain_models/olap", "storage_impl/olap", "scheduler/olap", "api_models/olap", "dep:analytics"] oltp = ["storage_impl/oltp"] kv_store = ["scheduler/kv_store"] @@ -114,7 +115,7 @@ analytics = { version = "0.1.0", path = "../analytics", optional = true } api_models = { version = "0.1.0", path = "../api_models", features = ["errors"] } cards = { version = "0.1.0", path = "../cards" } common_enums = { version = "0.1.0", path = "../common_enums" } -common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics", "keymanager"] } +common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals", "async_ext", "logs", "metrics", "keymanager", "encryption_service"] } currency_conversion = { version = "0.1.0", path = "../currency_conversion" } diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] } euclid = { version = "0.1.0", path = "../euclid", features = ["valued_jit"] } diff --git a/crates/router/src/bin/scheduler.rs b/crates/router/src/bin/scheduler.rs index 61f9c473a226..269c7d5b3b4a 100644 --- a/crates/router/src/bin/scheduler.rs +++ b/crates/router/src/bin/scheduler.rs @@ -38,7 +38,11 @@ async fn main() -> CustomResult<(), ProcessTrackerError> { let api_client = Box::new( services::ProxyClient::new( conf.proxy.clone(), - services::proxy_bypass_urls(&conf.locker, &conf.proxy.bypass_proxy_urls), + services::proxy_bypass_urls( + conf.key_manager.get_inner(), + &conf.locker, + &conf.proxy.bypass_proxy_urls, + ), ) .change_context(ProcessTrackerError::ConfigurationError)?, ); diff --git a/crates/router/src/consts.rs b/crates/router/src/consts.rs index d3091875f485..bdf8db2828e9 100644 --- a/crates/router/src/consts.rs +++ b/crates/router/src/consts.rs @@ -2,6 +2,7 @@ pub mod opensearch; #[cfg(feature = "olap")] pub mod user; pub mod user_role; +use common_utils::consts; pub use hyperswitch_interfaces::consts::{NO_ERROR_CODE, NO_ERROR_MESSAGE}; // ID generation pub(crate) const ID_LENGTH: usize = 20; @@ -43,8 +44,9 @@ pub(crate) const CANNOT_CONTINUE_AUTH: &str = pub(crate) const DEFAULT_NOTIFICATION_SCRIPT_LANGUAGE: &str = "en-US"; // General purpose base64 engines -pub(crate) const BASE64_ENGINE: base64::engine::GeneralPurpose = - base64::engine::general_purpose::STANDARD; + +pub(crate) const BASE64_ENGINE: base64::engine::GeneralPurpose = consts::BASE64_ENGINE; + pub(crate) const BASE64_ENGINE_URL_SAFE: base64::engine::GeneralPurpose = base64::engine::general_purpose::URL_SAFE; diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index 8a7854d658bf..a851be95d5ed 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -8,9 +8,8 @@ use common_utils::{ date_time, ext_traits::{AsyncExt, ConfigExt, Encode, ValueExt}, id_type, pii, + types::keymanager as km_types, }; -#[cfg(all(feature = "keymanager_create", feature = "olap"))] -use common_utils::{keymanager, types::keymanager as km_types}; use diesel_models::configs; use error_stack::{report, FutureExt, ResultExt}; use futures::future::try_join_all; @@ -111,6 +110,9 @@ pub async fn create_merchant_account( state: SessionState, req: api::MerchantAccountCreate, ) -> RouterResponse { + #[cfg(feature = "keymanager_create")] + use common_utils::keymanager; + let db = state.store.as_ref(); let key = services::generate_aes256_key() @@ -119,27 +121,16 @@ pub async fn create_merchant_account( let master_key = db.get_master_key(); + let key_manager_state = &(&state).into(); let merchant_id = req.get_merchant_reference_id().get_string_repr().to_owned(); - - let key_store = domain::MerchantKeyStore { - merchant_id: merchant_id.clone(), - key: domain_types::encrypt(key.to_vec().into(), master_key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to decrypt data from key store")?, - created_at: date_time::now(), - }; - - let domain_merchant_account = req - .create_domain_model_from_request(db, key_store.clone()) - .await?; + let identifier = km_types::Identifier::Merchant(merchant_id.clone()); #[cfg(feature = "keymanager_create")] { keymanager::create_key_in_key_manager( - &(&state).into(), + key_manager_state, km_types::EncryptionCreateRequest { - identifier: km_types::Identifier::Merchant(merchant_id.clone()), + identifier: identifier.clone(), }, ) .await @@ -147,12 +138,34 @@ pub async fn create_merchant_account( .attach_printable("Failed to insert key to KeyManager")?; } - db.insert_merchant_key_store(key_store.clone(), &master_key.to_vec().into()) + let key_store = domain::MerchantKeyStore { + merchant_id: merchant_id.clone(), + key: domain_types::encrypt( + key_manager_state, + key.to_vec().into(), + identifier.clone(), + master_key, + ) .await - .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?; + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to decrypt data from key store")?, + created_at: date_time::now(), + }; + + let domain_merchant_account = req + .create_domain_model_from_request(&state, key_store.clone()) + .await?; + let key_manager_state = &(&state).into(); + db.insert_merchant_key_store( + key_manager_state, + key_store.clone(), + &master_key.to_vec().into(), + ) + .await + .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?; let merchant_account = db - .insert_merchant(domain_merchant_account, &key_store) + .insert_merchant(key_manager_state, domain_merchant_account, &key_store) .await .to_duplicate_response(errors::ApiErrorResponse::DuplicateMerchantAccount)?; @@ -172,7 +185,7 @@ pub async fn create_merchant_account( trait MerchantAccountCreateBridge { async fn create_domain_model_from_request( self, - db: &dyn StorageInterface, + state: &SessionState, key: domain::MerchantKeyStore, ) -> RouterResult; } @@ -182,9 +195,10 @@ trait MerchantAccountCreateBridge { impl MerchantAccountCreateBridge for api::MerchantAccountCreate { async fn create_domain_model_from_request( self, - db: &dyn StorageInterface, + state: &SessionState, key_store: domain::MerchantKeyStore, ) -> RouterResult { + let db = &*state.store; let publishable_key = create_merchant_publishable_key(); let primary_business_details = self.get_primary_details_as_value().change_context( @@ -229,7 +243,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { let payment_response_hash_key = self.get_payment_response_hash_key(); let parent_merchant_id = get_parent_merchant( - db, + state, self.sub_merchants_enabled, self.parent_merchant_id, &key_store, @@ -241,6 +255,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { .await?; let key = key_store.key.clone().into_inner(); + let key_manager_state = state.into(); let mut merchant_account = async { Ok::<_, error_stack::Report>( @@ -248,10 +263,24 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { merchant_id: self.merchant_id.get_string_repr().to_owned(), merchant_name: self .merchant_name - .async_lift(|inner| domain_types::encrypt_optional(inner, key.peek())) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key.peek(), + ) + }) .await?, merchant_details: merchant_details - .async_lift(|inner| domain_types::encrypt_optional(inner, key.peek())) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key.peek(), + ) + }) .await?, return_url: self.return_url.map(|a| a.to_string()), webhook_details, @@ -293,7 +322,7 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { .change_context(errors::ApiErrorResponse::InternalServerError)?; CreateBusinessProfile::new(self.primary_business_details.clone()) - .create_business_profiles(db, &mut merchant_account, &key_store) + .create_business_profiles(state, &mut merchant_account, &key_store) .await?; Ok(merchant_account) @@ -385,7 +414,7 @@ impl CreateBusinessProfile { async fn create_business_profiles( &self, - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &mut domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> RouterResult<()> { @@ -394,7 +423,7 @@ impl CreateBusinessProfile { primary_business_details, } => { let business_profiles = Self::create_business_profiles_for_each_business_details( - db, + state, merchant_account.clone(), primary_business_details, key_store, @@ -410,7 +439,7 @@ impl CreateBusinessProfile { } Self::CreateDefaultBusinessProfile => { let business_profile = self - .create_default_business_profile(db, merchant_account.clone(), key_store) + .create_default_business_profile(state, merchant_account.clone(), key_store) .await?; merchant_account.default_profile = Some(business_profile.profile_id); @@ -423,12 +452,12 @@ impl CreateBusinessProfile { /// Create default business profile async fn create_default_business_profile( &self, - db: &dyn StorageInterface, + state: &SessionState, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> RouterResult { let business_profile = create_and_insert_business_profile( - db, + state, api_models::admin::BusinessProfileCreate::default(), merchant_account.clone(), key_store, @@ -442,7 +471,7 @@ impl CreateBusinessProfile { /// If there is no default profile in merchant account and only one primary_business_detail /// is available, then create a default business profile. async fn create_business_profiles_for_each_business_details( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: domain::MerchantAccount, primary_business_details: &Vec, key_store: &domain::MerchantKeyStore, @@ -462,7 +491,7 @@ impl CreateBusinessProfile { }; create_and_insert_business_profile( - db, + state, business_profile_create_request, merchant_account.clone(), key_store, @@ -486,10 +515,11 @@ impl CreateBusinessProfile { impl MerchantAccountCreateBridge for api::MerchantAccountCreate { async fn create_domain_model_from_request( self, - db: &dyn StorageInterface, + state: &SessionState, key_store: domain::MerchantKeyStore, ) -> RouterResult { let publishable_key = create_merchant_publishable_key(); + let db = &*state.store; let metadata = self.get_metadata_as_secret().change_context( errors::ApiErrorResponse::InvalidDataValue { @@ -514,24 +544,36 @@ impl MerchantAccountCreateBridge for api::MerchantAccountCreate { .await?; let key = key_store.key.into_inner(); + let merchant_id = self + .get_merchant_reference_id() + .get_string_repr() + .to_owned(); + let key_manager_state = state.into(); + let identifier = km_types::Identifier::Merchant(merchant_id.clone()); async { Ok::<_, error_stack::Report>( domain::MerchantAccount { - merchant_id: self - .get_merchant_reference_id() - .get_string_repr() - .to_owned(), + merchant_id, merchant_name: Some( domain_types::encrypt( + &key_manager_state, self.merchant_name .map(|merchant_name| merchant_name.into_inner()), + identifier.clone(), key.peek(), ) .await?, ), merchant_details: merchant_details - .async_lift(|inner| domain_types::encrypt_optional(inner, key.peek())) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + identifier.clone(), + key.peek(), + ) + }) .await?, return_url: None, webhook_details: None, @@ -577,7 +619,7 @@ pub async fn list_merchant_account( ) -> RouterResponse> { let merchant_accounts = state .store - .list_merchant_accounts_by_organization_id(&req.organization_id) + .list_merchant_accounts_by_organization_id(&(&state).into(), &req.organization_id) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -600,8 +642,10 @@ pub async fn get_merchant_account( req: api::MerchantId, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &req.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -609,7 +653,7 @@ pub async fn get_merchant_account( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(&req.merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &req.merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -623,13 +667,15 @@ pub async fn get_merchant_account( /// For backwards compatibility, whenever new business labels are passed in /// primary_business_details, create a business profile pub async fn create_business_profile_from_business_labels( + state: &SessionState, db: &dyn StorageInterface, key_store: &domain::MerchantKeyStore, merchant_id: &str, new_business_details: Vec, ) -> RouterResult<()> { + let key_manager_state = &state.into(); let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -657,7 +703,7 @@ pub async fn create_business_profile_from_business_labels( }; let business_profile_create_result = create_and_insert_business_profile( - db, + state, business_profile_create_request, merchant_account.clone(), key_store, @@ -673,9 +719,14 @@ pub async fn create_business_profile_from_business_labels( // If a business_profile is created, then unset the default profile if business_profile_create_result.is_ok() && merchant_account.default_profile.is_some() { let unset_default_profile = domain::MerchantAccountUpdate::UnsetDefaultProfile; - db.update_merchant(merchant_account.clone(), unset_default_profile, key_store) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + db.update_merchant( + key_manager_state, + merchant_account.clone(), + unset_default_profile, + key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; } } @@ -758,8 +809,10 @@ pub async fn merchant_account_update( req: api::MerchantAccountUpdate, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &req.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -817,6 +870,7 @@ pub async fn merchant_account_update( .clone() .async_map(|primary_business_details| async { let _ = create_business_profile_from_business_labels( + &state, db, &key_store, merchant_id, @@ -845,11 +899,14 @@ pub async fn merchant_account_update( // Update the business profile, This is for backwards compatibility update_business_profile_cascade(state.clone(), req.clone(), merchant_id.to_string()).await?; + let identifier = km_types::Identifier::Merchant(key_store.merchant_id.clone()); let updated_merchant_account = storage::MerchantAccountUpdate::Update { merchant_name: req .merchant_name .map(Secret::new) - .async_lift(|inner| domain_types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain_types::encrypt_optional(key_manager_state, inner, identifier.clone(), key) + }) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt merchant name")?, @@ -862,7 +919,9 @@ pub async fn merchant_account_update( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to convert merchant_details to a value")? .map(Secret::new) - .async_lift(|inner| domain_types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain_types::encrypt_optional(key_manager_state, inner, identifier.clone(), key) + }) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt merchant details")?, @@ -880,7 +939,7 @@ pub async fn merchant_account_update( sub_merchants_enabled: req.sub_merchants_enabled, parent_merchant_id: get_parent_merchant( - db, + &state, req.sub_merchants_enabled, req.parent_merchant_id, &key_store, @@ -905,7 +964,12 @@ pub async fn merchant_account_update( }; let response = db - .update_specific_fields_in_merchant(merchant_id, updated_merchant_account, &key_store) + .update_specific_fields_in_merchant( + key_manager_state, + merchant_id, + updated_merchant_account, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -924,9 +988,10 @@ pub async fn merchant_account_delete( ) -> RouterResponse { let mut is_deleted = false; let db = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let merchant_key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -934,7 +999,7 @@ pub async fn merchant_account_delete( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &merchant_key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &merchant_key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -988,7 +1053,7 @@ pub async fn merchant_account_delete( } async fn get_parent_merchant( - db: &dyn StorageInterface, + state: &SessionState, sub_merchants_enabled: Option, parent_merchant: Option, key_store: &domain::MerchantKeyStore, @@ -1004,7 +1069,7 @@ async fn get_parent_merchant( message: "If `sub_merchants_enabled` is `true`, then `parent_merchant_id` is mandatory".to_string(), }) }) - .map(|id| validate_merchant_id(db, id,key_store).change_context( + .map(|id| validate_merchant_id(state, id,key_store).change_context( errors::ApiErrorResponse::InvalidDataValue { field_name: "parent_merchant_id" } ))? .await? @@ -1016,11 +1081,12 @@ async fn get_parent_merchant( } async fn validate_merchant_id>( - db: &dyn StorageInterface, + state: &SessionState, merchant_id: S, key_store: &domain::MerchantKeyStore, ) -> RouterResult { - db.find_merchant_account_by_merchant_id(&merchant_id.into(), key_store) + let db = &*state.store; + db.find_merchant_account_by_merchant_id(&state.into(), &merchant_id.into(), key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) } @@ -1066,10 +1132,12 @@ pub async fn create_payment_connector( merchant_id: &String, ) -> RouterResponse { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); #[cfg(feature = "dummy_connector")] validate_dummy_connector_enabled(&state, &req.connector_name).await?; let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -1083,7 +1151,7 @@ pub async fn create_payment_connector( let merchant_account = state .store - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1221,6 +1289,7 @@ pub async fn create_payment_connector( state .store .update_specific_fields_in_merchant( + key_manager_state, merchant_id, storage::MerchantAccountUpdate::ModifiedAtUpdate, &key_store, @@ -1242,7 +1311,7 @@ pub async fn create_payment_connector( if let Some(val) = req.pm_auth_config.clone() { validate_pm_auth( val, - &*state.clone().store, + &state, merchant_id.clone().as_str(), &key_store, merchant_account, @@ -1256,14 +1325,15 @@ pub async fn create_payment_connector( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encoding ConnectorAuthType to serde_json::Value")?; let conn_auth = Secret::new(connector_auth); - + let identifier = km_types::Identifier::Merchant(key_store.merchant_id.clone()); let merchant_connector_account = domain::MerchantConnectorAccount { merchant_id: merchant_id.to_string(), connector_type: req.connector_type, connector_name: req.connector_name.to_string(), merchant_connector_id: utils::generate_id(consts::ID_LENGTH, "mca"), - connector_account_details: domain_types::encrypt( + connector_account_details: domain_types::encrypt(key_manager_state, conn_auth, + identifier.clone(), key_store.key.peek(), ) .await @@ -1296,10 +1366,11 @@ pub async fn create_payment_connector( applepay_verified_domains: None, pm_auth_config: req.pm_auth_config.clone(), status: connector_status, - connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(&key_store, &req.metadata).await?, + connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details(&state, &key_store, &req.metadata).await?, additional_merchant_data: if let Some(mcd) = merchant_recipient_data { - Some(domain_types::encrypt( + Some(domain_types::encrypt(key_manager_state, Secret::new(mcd), + identifier, key_store.key.peek(), ) .await @@ -1329,7 +1400,11 @@ pub async fn create_payment_connector( let mca = state .store - .insert_merchant_connector_account(merchant_connector_account, &key_store) + .insert_merchant_connector_account( + key_manager_state, + merchant_connector_account, + &key_store, + ) .await .to_duplicate_response( errors::ApiErrorResponse::DuplicateMerchantConnectorAccount { @@ -1382,7 +1457,7 @@ pub async fn create_payment_connector( async fn validate_pm_auth( val: serde_json::Value, - db: &dyn StorageInterface, + state: &SessionState, merchant_id: &str, key_store: &domain::MerchantKeyStore, merchant_account: domain::MerchantAccount, @@ -1394,8 +1469,10 @@ async fn validate_pm_auth( }) .attach_printable("Failed to deserialize Payment Method Auth config")?; - let all_mcas = db + let all_mcas = &*state + .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, @@ -1407,8 +1484,7 @@ async fn validate_pm_auth( for conn_choice in config.enabled_payment_methods { let pm_auth_mca = all_mcas - .clone() - .into_iter() + .iter() .find(|mca| mca.merchant_connector_id == conn_choice.mca_id) .ok_or(errors::ApiErrorResponse::GenericNotFoundError { message: "payment method auth connector account not found".to_string(), @@ -1432,8 +1508,10 @@ pub async fn retrieve_payment_connector( merchant_connector_id: String, ) -> RouterResponse { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &store.get_master_key().to_vec().into(), ) @@ -1441,12 +1519,13 @@ pub async fn retrieve_payment_connector( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let _merchant_account = store - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let mca = store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_id, &merchant_connector_id, &key_store, @@ -1464,8 +1543,10 @@ pub async fn list_payment_connectors( merchant_id: String, ) -> RouterResponse> { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &store.get_master_key().to_vec().into(), ) @@ -1474,12 +1555,13 @@ pub async fn list_payment_connectors( // Validate merchant account store - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_connector_accounts = store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, &merchant_id, true, &key_store, @@ -1503,18 +1585,24 @@ pub async fn update_payment_connector( req: api_models::admin::MerchantConnectorUpdate, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id, merchant_connector_id, &key_store, @@ -1559,7 +1647,7 @@ pub async fn update_payment_connector( if let Some(val) = req.pm_auth_config.clone() { validate_pm_auth( val, - db, + &state, merchant_id, &key_store, merchant_account, @@ -1568,7 +1656,6 @@ pub async fn update_payment_connector( .await?; } } - let payment_connector = storage::MerchantConnectorAccountUpdate::Update { merchant_id: None, connector_type: Some(req.connector_type), @@ -1578,7 +1665,12 @@ pub async fn update_payment_connector( connector_account_details: req .connector_account_details .async_lift(|inner| { - domain_types::encrypt_optional(inner, key_store.key.get_inner().peek()) + domain_types::encrypt_optional( + key_manager_state, + inner, + km_types::Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.get_inner().peek(), + ) }) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1600,7 +1692,7 @@ pub async fn update_payment_connector( pm_auth_config: req.pm_auth_config, status: Some(connector_status), connector_wallets_details: helpers::get_encrypted_apple_pay_connector_wallets_details( - &key_store, &metadata, + &state, &key_store, &metadata, ) .await?, }; @@ -1615,7 +1707,12 @@ pub async fn update_payment_connector( let request_connector_label = req.connector_label; let updated_mca = db - .update_merchant_connector_account(mca, payment_connector.into(), &key_store) + .update_merchant_connector_account( + key_manager_state, + mca, + payment_connector.into(), + &key_store, + ) .await .change_context( errors::ApiErrorResponse::DuplicateMerchantConnectorAccount { @@ -1638,18 +1735,24 @@ pub async fn delete_payment_connector( merchant_connector_id: String, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let _merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let _mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_id, &merchant_connector_id, &key_store, @@ -1683,14 +1786,19 @@ pub async fn kv_for_merchant( enable: bool, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // check if the merchant account exists let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1707,6 +1815,7 @@ pub async fn kv_for_merchant( } db.update_merchant( + key_manager_state, merchant_account, storage::MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme: MerchantStorageScheme::RedisKv, @@ -1717,6 +1826,7 @@ pub async fn kv_for_merchant( } (false, MerchantStorageScheme::RedisKv) => { db.update_merchant( + key_manager_state, merchant_account, storage::MerchantAccountUpdate::StorageSchemeUpdate { storage_scheme: MerchantStorageScheme::PostgresOnly, @@ -1779,14 +1889,19 @@ pub async fn check_merchant_account_kv_status( merchant_id: String, ) -> RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // check if the merchant account exists let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1825,17 +1940,19 @@ pub fn get_frm_config_as_secret( } pub async fn create_and_insert_business_profile( - db: &dyn StorageInterface, + state: &SessionState, request: api::BusinessProfileCreate, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> RouterResult { let business_profile_new = - admin::create_business_profile(merchant_account, request, key_store).await?; + admin::create_business_profile(state, merchant_account, request, key_store).await?; let profile_name = business_profile_new.profile_name.clone(); - db.insert_business_profile(business_profile_new) + state + .store + .insert_business_profile(business_profile_new) .await .to_duplicate_response(errors::ApiErrorResponse::GenericDuplicateError { message: format!( @@ -1859,15 +1976,20 @@ pub async fn create_business_profile( } let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; // Get the merchant account, if few fields are not passed, then they will be inherited from // merchant account let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1882,18 +2004,23 @@ pub async fn create_business_profile( } let business_profile = - create_and_insert_business_profile(db, request, merchant_account.clone(), &key_store) + create_and_insert_business_profile(&state, request, merchant_account.clone(), &key_store) .await?; if merchant_account.default_profile.is_some() { let unset_default_profile = domain::MerchantAccountUpdate::UnsetDefaultProfile; - db.update_merchant(merchant_account, unset_default_profile, &key_store) - .await - .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; + db.update_merchant( + key_manager_state, + merchant_account, + unset_default_profile, + &key_store, + ) + .await + .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; } Ok(service_api::ApplicationResponse::Json( - admin::business_profile_response(business_profile, &key_store) + admin::business_profile_response(&state, business_profile, &key_store) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details") .await?, @@ -1906,7 +2033,11 @@ pub async fn list_business_profile( ) -> RouterResponse> { let db = state.store.as_ref(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + &(&state).into(), + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let profiles = db @@ -1916,7 +2047,7 @@ pub async fn list_business_profile( .clone(); let mut business_profiles = Vec::new(); for profile in profiles { - let business_profile = admin::business_profile_response(profile, &key_store) + let business_profile = admin::business_profile_response(&state, profile, &key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details")?; @@ -1933,7 +2064,11 @@ pub async fn retrieve_business_profile( ) -> RouterResponse { let db = state.store.as_ref(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + &(&state).into(), + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let business_profile = db @@ -1944,7 +2079,7 @@ pub async fn retrieve_business_profile( })?; Ok(service_api::ApplicationResponse::Json( - admin::business_profile_response(business_profile, &key_store) + admin::business_profile_response(&state, business_profile, &key_store) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details") .await?, @@ -1982,6 +2117,7 @@ pub async fn update_business_profile( })?; let key_store = db .get_merchant_key_store_by_merchant_id( + &(&state).into(), merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -2051,7 +2187,7 @@ pub async fn update_business_profile( .map(Secret::new); let outgoing_webhook_custom_http_headers = request .outgoing_webhook_custom_http_headers - .async_map(|headers| create_encrypted_data(&key_store, headers)) + .async_map(|headers| create_encrypted_data(&state, &key_store, headers)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -2119,7 +2255,7 @@ pub async fn update_business_profile( })?; Ok(service_api::ApplicationResponse::Json( - admin::business_profile_response(updated_business_profile, &key_store) + admin::business_profile_response(&state, updated_business_profile, &key_store) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to parse business profile details") .await?, diff --git a/crates/router/src/core/api_keys.rs b/crates/router/src/core/api_keys.rs index df8a55cd7184..6af82a6b7a84 100644 --- a/crates/router/src/core/api_keys.rs +++ b/crates/router/src/core/api_keys.rs @@ -122,6 +122,7 @@ pub async fn create_api_key( // non-existence of a merchant account. store .get_merchant_key_store_by_merchant_id( + &(&state).into(), merchant_id.as_str(), &store.get_master_key().to_vec().into(), ) diff --git a/crates/router/src/core/apple_pay_certificates_migration.rs b/crates/router/src/core/apple_pay_certificates_migration.rs index 327358bda504..87c159357150 100644 --- a/crates/router/src/core/apple_pay_certificates_migration.rs +++ b/crates/router/src/core/apple_pay_certificates_migration.rs @@ -1,5 +1,5 @@ use api_models::apple_pay_certificates_migration; -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, types::keymanager::Identifier}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -28,11 +28,12 @@ pub async fn apple_pay_certificates_migration( let mut migration_successful_merchant_ids = vec![]; let mut migration_failed_merchant_ids = vec![]; - + let key_manager_state = &(&state).into(); for merchant_id in merchant_id_list { let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -41,6 +42,7 @@ pub async fn apple_pay_certificates_migration( let merchant_connector_accounts = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, merchant_id, true, &key_store, @@ -63,11 +65,13 @@ pub async fn apple_pay_certificates_migration( .ok(); if let Some(apple_pay_metadata) = connector_apple_pay_metadata { let encrypted_apple_pay_metadata = domain_types::encrypt( + &(&state).into(), Secret::new( serde_json::to_value(apple_pay_metadata) .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to serialize apple pay metadata as JSON")?, ), + Identifier::Merchant(merchant_id.clone()), key_store.key.get_inner().peek(), ) .await diff --git a/crates/router/src/core/blocklist/utils.rs b/crates/router/src/core/blocklist/utils.rs index 97324effa805..c739a86ee513 100644 --- a/crates/router/src/core/blocklist/utils.rs +++ b/crates/router/src/core/blocklist/utils.rs @@ -398,6 +398,7 @@ where if should_payment_be_blocked { // Update db for attempt and intent status. db.update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::RejectUpdate { status: common_enums::IntentStatus::Failed, diff --git a/crates/router/src/core/cards_info.rs b/crates/router/src/core/cards_info.rs index b0c1aca53228..98719b5a1e6a 100644 --- a/crates/router/src/core/cards_info.rs +++ b/crates/router/src/core/cards_info.rs @@ -30,7 +30,7 @@ pub async fn retrieve_card_info( verify_iin_length(&request.card_iin)?; helpers::verify_payment_intent_time_and_client_secret( - db, + &state, &merchant_account, &key_store, request.client_secret, diff --git a/crates/router/src/core/conditional_config.rs b/crates/router/src/core/conditional_config.rs index f740c6dfcc25..c34f8e7116dc 100644 --- a/crates/router/src/core/conditional_config.rs +++ b/crates/router/src/core/conditional_config.rs @@ -102,7 +102,7 @@ pub async fn upsert_conditional_config( algo_id.update_conditional_config_id(key.clone()); let config_key = cache::CacheKind::DecisionManager(key.into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -138,7 +138,7 @@ pub async fn upsert_conditional_config( algo_id.update_conditional_config_id(key.clone()); let config_key = cache::CacheKind::DecisionManager(key.into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -168,7 +168,7 @@ pub async fn delete_conditional_config( .unwrap_or_default(); algo_id.config_algo_id = None; let config_key = cache::CacheKind::DecisionManager(key.clone().into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update deleted algorithm ref")?; diff --git a/crates/router/src/core/customers.rs b/crates/router/src/core/customers.rs index c0e0291b8c46..e9126200acce 100644 --- a/crates/router/src/core/customers.rs +++ b/crates/router/src/core/customers.rs @@ -1,10 +1,12 @@ +use api_models::customers::CustomerRequestWithEmail; use common_utils::{ crypto::{Encryptable, GcmAes256}, errors::ReportSwitchExt, ext_traits::OptionExt, + types::keymanager::{Identifier, ToEncryptable}, }; use error_stack::{report, ResultExt}; -use masking::ExposeInterface; +use masking::{Secret, SwitchStrategy}; use router_env::{instrument, tracing}; use crate::{ @@ -19,7 +21,7 @@ use crate::{ api::customers, domain::{ self, - types::{self, AsyncLift, TypeEncryption}, + types::{self, TypeEncryption}, }, storage::{self, enums}, }, @@ -43,6 +45,7 @@ pub async fn create_customer( let merchant_id = &merchant_account.merchant_id; merchant_id.clone_into(&mut customer_data.merchant_id); + let key_manager_state = &(&state).into(); // We first need to validate whether the customer with the given customer id already exists // this may seem like a redundant db call, as the insert_customer will anyway return this error @@ -51,6 +54,7 @@ pub async fn create_customer( // it errors out, now the address that was inserted is not deleted match db .find_customer_by_customer_id_merchant_id( + key_manager_state, &customer_id, merchant_id, &key_store, @@ -76,6 +80,7 @@ pub async fn create_customer( let address = customer_data .get_domain_address( + &state, customer_address, merchant_id, &customer_id, @@ -87,7 +92,7 @@ pub async fn create_customer( .attach_printable("Failed while encrypting address")?; Some( - db.insert_address_for_customers(address, &key_store) + db.insert_address_for_customers(key_manager_state, address, &key_store) .await .switch() .attach_printable("Failed while inserting new address")?, @@ -96,40 +101,46 @@ pub async fn create_customer( None }; - let new_customer = async { - Ok(domain::Customer { - customer_id: customer_id.to_owned(), - merchant_id: merchant_id.to_string(), - name: customer_data - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: customer_data - .email - .async_lift(|inner| types::encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, - phone: customer_data - .phone - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - description: customer_data.description, - phone_country_code: customer_data.phone_country_code, - metadata: customer_data.metadata, - id: None, - connector_customer: None, - address_id: address.clone().map(|addr| addr.address_id), - created_at: common_utils::date_time::now(), - modified_at: common_utils::date_time::now(), - default_payment_method_id: None, - updated_by: None, - }) - } + let encrypted_data = types::batch_encrypt( + &(&state).into(), + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: customer_data.name.clone(), + email: customer_data.email.clone(), + phone: customer_data.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) .await .switch() .attach_printable("Failed while encrypting Customer")?; + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::CustomersErrorResponse::InternalServerError)?; + let new_customer = domain::Customer { + customer_id: customer_id.to_owned(), + merchant_id: merchant_id.to_string(), + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, + description: customer_data.description, + phone_country_code: customer_data.phone_country_code, + metadata: customer_data.metadata, + id: None, + connector_customer: None, + address_id: address.clone().map(|addr| addr.address_id), + created_at: common_utils::date_time::now(), + modified_at: common_utils::date_time::now(), + default_payment_method_id: None, + updated_by: None, + }; let customer = db - .insert_customer(new_customer, &key_store, merchant_account.storage_scheme) + .insert_customer( + key_manager_state, + new_customer, + &key_store, + merchant_account.storage_scheme, + ) .await .to_duplicate_response(errors::CustomersErrorResponse::CustomerAlreadyExists)?; @@ -148,8 +159,10 @@ pub async fn retrieve_customer( req: customers::CustomerId, ) -> errors::CustomerResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let response = db .find_customer_by_customer_id_merchant_id( + key_manager_state, &req.customer_id, &merchant_account.merchant_id, &key_store, @@ -159,7 +172,7 @@ pub async fn retrieve_customer( .switch()?; let address = match &response.address_id { Some(address_id) => Some(api_models::payments::AddressDetails::from( - db.find_address_by_address_id(address_id, &key_store) + db.find_address_by_address_id(key_manager_state, address_id, &key_store) .await .switch()?, )), @@ -179,7 +192,7 @@ pub async fn list_customers( let db = state.store.as_ref(); let domain_customers = db - .list_customers_by_merchant_id(&merchant_id, &key_store) + .list_customers_by_merchant_id(&(&state).into(), &merchant_id, &key_store) .await .switch()?; @@ -199,9 +212,10 @@ pub async fn delete_customer( key_store: domain::MerchantKeyStore, ) -> errors::CustomerResponse { let db = &state.store; - + let key_manager_state = &(&state).into(); let customer_orig = db .find_customer_by_customer_id_merchant_id( + key_manager_state, &req.customer_id, &merchant_account.merchant_id, &key_store, @@ -262,17 +276,24 @@ pub async fn delete_customer( }; let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let redacted_encrypted_value: Encryptable> = Encryptable::encrypt_via_api( + key_manager_state, + REDACTED.to_string().into(), + identifier.clone(), + key, + GcmAes256, + ) + .await + .switch()?; - let redacted_encrypted_value: Encryptable> = - Encryptable::encrypt(REDACTED.to_string().into(), key, GcmAes256) - .await - .switch()?; - - let redacted_encrypted_email: Encryptable< - masking::Secret<_, common_utils::pii::EmailStrategy>, - > = Encryptable::encrypt(REDACTED.to_string().into(), key, GcmAes256) - .await - .switch()?; + let redacted_encrypted_email = Encryptable::new( + redacted_encrypted_value + .clone() + .into_inner() + .switch_strategy(), + redacted_encrypted_value.clone().into_encrypted(), + ); let update_address = storage::AddressUpdate::Update { city: Some(REDACTED.to_string()), @@ -292,6 +313,7 @@ pub async fn delete_customer( match db .update_address_by_merchant_id_customer_id( + key_manager_state, &req.customer_id, &merchant_account.merchant_id, update_address, @@ -314,9 +336,15 @@ pub async fn delete_customer( let updated_customer = storage::CustomerUpdate::Update { name: Some(redacted_encrypted_value.clone()), email: Some( - Encryptable::encrypt(REDACTED.to_string().into(), key, GcmAes256) - .await - .switch()?, + Encryptable::encrypt_via_api( + key_manager_state, + REDACTED.to_string().into(), + identifier, + key, + GcmAes256, + ) + .await + .switch()?, ), phone: Box::new(Some(redacted_encrypted_value.clone())), description: Some(REDACTED.to_string()), @@ -326,6 +354,7 @@ pub async fn delete_customer( address_id: None, }; db.update_customer_by_customer_id_merchant_id( + key_manager_state, req.customer_id.clone(), merchant_account.merchant_id, customer_orig, @@ -362,9 +391,10 @@ pub async fn update_customer( .get_required_value("customer_id") .change_context(errors::CustomersErrorResponse::InternalServerError) .attach("Missing required field `customer_id`")?; - + let key_manager_state = &(&state).into(); let customer = db .find_customer_by_customer_id_merchant_id( + key_manager_state, customer_id, &merchant_account.merchant_id, &key_store, @@ -380,12 +410,18 @@ pub async fn update_customer( Some(address_id) => { let customer_address: api_models::payments::AddressDetails = addr.clone(); let update_address = update_customer - .get_address_update(customer_address, key, merchant_account.storage_scheme) + .get_address_update( + &state, + customer_address, + key, + merchant_account.storage_scheme, + merchant_account.merchant_id.clone(), + ) .await .switch() .attach_printable("Failed while encrypting Address while Update")?; Some( - db.update_address(address_id, update_address, &key_store) + db.update_address(key_manager_state, address_id, update_address, &key_store) .await .switch() .attach_printable(format!( @@ -399,6 +435,7 @@ pub async fn update_customer( let address = update_customer .get_domain_address( + &state, customer_address, &merchant_account.merchant_id, &customer.customer_id, @@ -409,7 +446,7 @@ pub async fn update_customer( .switch() .attach_printable("Failed while encrypting address")?; Some( - db.insert_address_for_customers(address, &key_store) + db.insert_address_for_customers(key_manager_state, address, &key_store) .await .switch() .attach_printable("Failed while inserting new address")?, @@ -419,47 +456,44 @@ pub async fn update_customer( } else { match &customer.address_id { Some(address_id) => Some( - db.find_address_by_address_id(address_id, &key_store) + db.find_address_by_address_id(key_manager_state, address_id, &key_store) .await .switch()?, ), None => None, } }; + let encrypted_data = types::batch_encrypt( + &(&state).into(), + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: update_customer.name.clone(), + email: update_customer.email.clone(), + phone: update_customer.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .switch()?; + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::CustomersErrorResponse::InternalServerError)?; let response = db .update_customer_by_customer_id_merchant_id( + key_manager_state, customer_id.to_owned(), merchant_account.merchant_id.to_owned(), customer, - async { - Ok(storage::CustomerUpdate::Update { - name: update_customer - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: update_customer - .email - .async_lift(|inner| { - types::encrypt_optional(inner.map(|inner| inner.expose()), key) - }) - .await?, - phone: Box::new( - update_customer - .phone - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - ), - phone_country_code: update_customer.phone_country_code, - metadata: update_customer.metadata, - description: update_customer.description, - connector_customer: None, - address_id: address.clone().map(|addr| addr.address_id), - }) - } - .await - .switch() - .attach_printable("Failed while encrypting while updating customer")?, + storage::CustomerUpdate::Update { + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: Box::new(encryptable_customer.phone), + phone_country_code: update_customer.phone_country_code, + metadata: update_customer.metadata, + description: update_customer.description, + connector_customer: None, + address_id: address.clone().map(|addr| addr.address_id), + }, &key_store, merchant_account.storage_scheme, ) diff --git a/crates/router/src/core/disputes.rs b/crates/router/src/core/disputes.rs index 2d2d7bbcaebf..6a7f531f5a01 100644 --- a/crates/router/src/core/disputes.rs +++ b/crates/router/src/core/disputes.rs @@ -89,6 +89,7 @@ pub async fn accept_dispute( )?; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &dispute.payment_id, &merchant_account.merchant_id, &key_store, @@ -202,6 +203,7 @@ pub async fn submit_evidence( .await?; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &dispute.payment_id, &merchant_account.merchant_id, &key_store, diff --git a/crates/router/src/core/encryption.rs b/crates/router/src/core/encryption.rs index 7f7945bfd1ca..efd7d3bfeaaa 100644 --- a/crates/router/src/core/encryption.rs +++ b/crates/router/src/core/encryption.rs @@ -14,7 +14,7 @@ pub async fn transfer_encryption_key( ) -> errors::CustomResult { let db = &*state.store; let key_stores = db - .get_all_key_stores(&db.get_master_key().to_vec().into()) + .get_all_key_stores(&state.into(), &db.get_master_key().to_vec().into()) .await .change_context(errors::ApiErrorResponse::InternalServerError)?; send_request_to_key_service_for_merchant(state, key_stores).await diff --git a/crates/router/src/core/files/helpers.rs b/crates/router/src/core/files/helpers.rs index 3a565865603d..ed95e7790e72 100644 --- a/crates/router/src/core/files/helpers.rs +++ b/crates/router/src/core/files/helpers.rs @@ -261,6 +261,7 @@ pub async fn upload_and_get_provider_provider_file_id_profile_id( let payment_intent = state .store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &dispute.payment_id, &merchant_account.merchant_id, key_store, diff --git a/crates/router/src/core/fraud_check.rs b/crates/router/src/core/fraud_check.rs index 331eb4302117..8ddd0b7aa242 100644 --- a/crates/router/src/core/fraud_check.rs +++ b/crates/router/src/core/fraud_check.rs @@ -123,7 +123,7 @@ where pub async fn should_call_frm( merchant_account: &domain::MerchantAccount, payment_data: &payments::PaymentData, - db: &dyn StorageInterface, + state: &SessionState, key_store: domain::MerchantKeyStore, ) -> RouterResult<( bool, @@ -136,6 +136,7 @@ where { match merchant_account.frm_routing_algorithm.clone() { Some(frm_routing_algorithm_value) => { + let db = &*state.store; let frm_routing_algorithm_struct: FrmRoutingAlgorithm = frm_routing_algorithm_value .clone() .parse_value("FrmRoutingAlgorithm") @@ -157,6 +158,7 @@ where let merchant_connector_account_from_db_option = db .find_merchant_connector_account_by_profile_id_connector_name( + &state.into(), &profile_id, &frm_routing_algorithm_struct.data, &key_store, @@ -417,7 +419,7 @@ where let frm_data_updated = fraud_check_operation .to_update_tracker()? .update_tracker( - &*state.store, + state, &key_store, frm_data.clone(), payment_data, @@ -492,7 +494,7 @@ where let mut frm_data = fraud_check_operation .to_update_tracker()? .update_tracker( - &*state.store, + state, &key_store, frm_data.to_owned(), payment_data, @@ -527,7 +529,7 @@ where let updated_frm_data = fraud_check_operation .to_update_tracker()? .update_tracker( - &*state.store, + state, &key_store, frm_data.to_owned(), payment_data, @@ -547,7 +549,6 @@ where #[allow(clippy::too_many_arguments)] pub async fn call_frm_before_connector_call<'a, F, Req>( - db: &dyn StorageInterface, operation: &BoxedOperation<'_, F, Req>, merchant_account: &domain::MerchantAccount, payment_data: &mut payments::PaymentData, @@ -562,13 +563,13 @@ where F: Send + Clone, { let (is_frm_enabled, frm_routing_algorithm, frm_connector_label, frm_configs) = - should_call_frm(merchant_account, payment_data, db, key_store.clone()).await?; + should_call_frm(merchant_account, payment_data, state, key_store.clone()).await?; if let Some((frm_routing_algorithm_val, profile_id)) = frm_routing_algorithm.zip(frm_connector_label) { if let Some(frm_configs) = frm_configs.clone() { let mut updated_frm_info = Box::pin(make_frm_data_and_fraud_check_operation( - db, + &*state.store, state, merchant_account, payment_data.to_owned(), @@ -655,6 +656,7 @@ pub async fn frm_fulfillment_core( let db = &*state.clone().store; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &req.payment_id.clone(), &merchant_account.merchant_id, &key_store, diff --git a/crates/router/src/core/fraud_check/operation.rs b/crates/router/src/core/fraud_check/operation.rs index 8c6f8b7bf52f..2d159d2e2209 100644 --- a/crates/router/src/core/fraud_check/operation.rs +++ b/crates/router/src/core/fraud_check/operation.rs @@ -14,7 +14,6 @@ use crate::{ errors::{self, RouterResult}, payments, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, types::{domain, fraud_check::FrmRouterData}, }; @@ -101,7 +100,7 @@ pub trait Domain: Send + Sync { pub trait UpdateTracker: Send { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, frm_data: D, payment_data: &mut payments::PaymentData, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs index 7c97eee8b490..698eb43385e2 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_post.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_post.rs @@ -19,7 +19,6 @@ use crate::{ }, payments, }, - db::StorageInterface, errors, routes::app::ReqState, services::{self, api}, @@ -329,13 +328,14 @@ impl Domain for FraudCheckPost { impl UpdateTracker for FraudCheckPost { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, mut frm_data: FrmData, payment_data: &mut payments::PaymentData, frm_suggestion: Option, frm_router_data: FrmRouterData, ) -> RouterResult { + let db = &*state.store; let frm_check_update = match frm_router_data.response { FrmResponse::Sale(response) => match response { Err(err) => Some(FraudCheckUpdate::ErrorUpdate { @@ -515,6 +515,7 @@ impl UpdateTracker for FraudCheckPost { payment_data.payment_intent = db .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), PaymentIntentUpdate::RejectUpdate { status: payment_intent_status, diff --git a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs index c153acc75446..6a0bd78130df 100644 --- a/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs +++ b/crates/router/src/core/fraud_check/operation/fraud_check_pre.rs @@ -16,7 +16,6 @@ use crate::{ }, payments, }, - db::StorageInterface, errors, routes::app::ReqState, types::{ @@ -218,7 +217,7 @@ impl Domain for FraudCheckPre { impl UpdateTracker for FraudCheckPre { async fn update_tracker<'b>( &'b self, - db: &dyn StorageInterface, + state: &SessionState, _key_store: &domain::MerchantKeyStore, mut frm_data: FrmData, payment_data: &mut payments::PaymentData, @@ -337,6 +336,7 @@ impl UpdateTracker for FraudCheckPre { }), }; + let db = &*state.store; frm_data.fraud_check = match frm_check_update { Some(fraud_check_update) => db .update_fraud_check_response_with_attempt_id( diff --git a/crates/router/src/core/locker_migration.rs b/crates/router/src/core/locker_migration.rs index 24ba06bcf362..22b3b29e03bd 100644 --- a/crates/router/src/core/locker_migration.rs +++ b/crates/router/src/core/locker_migration.rs @@ -17,10 +17,11 @@ pub async fn rust_locker_migration( merchant_id: &str, ) -> CustomResult, errors::ApiErrorResponse> { let db = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -28,13 +29,13 @@ pub async fn rust_locker_migration( .change_context(errors::ApiErrorResponse::InternalServerError)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) .change_context(errors::ApiErrorResponse::InternalServerError)?; let domain_customers = db - .list_customers_by_merchant_id(merchant_id, &key_store) + .list_customers_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError)?; diff --git a/crates/router/src/core/mandate/helpers.rs b/crates/router/src/core/mandate/helpers.rs index 2bb981d03e89..a4eb18371f0d 100644 --- a/crates/router/src/core/mandate/helpers.rs +++ b/crates/router/src/core/mandate/helpers.rs @@ -21,6 +21,7 @@ pub async fn get_profile_id_for_mandate( let pi = state .store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), payment_id, &merchant_account.merchant_id, key_store, diff --git a/crates/router/src/core/payment_link.rs b/crates/router/src/core/payment_link.rs index 49e4885dc93e..bf50405ba816 100644 --- a/crates/router/src/core/payment_link.rs +++ b/crates/router/src/core/payment_link.rs @@ -59,6 +59,7 @@ pub async fn initiate_payment_link_flow( let db = &*state.store; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, &merchant_id, &key_store, @@ -497,6 +498,7 @@ pub async fn get_payment_link_status( let db = &*state.store; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, &merchant_id, &key_store, diff --git a/crates/router/src/core/payment_methods.rs b/crates/router/src/core/payment_methods.rs index b9e21b616c0f..86cb1fc1b1e7 100644 --- a/crates/router/src/core/payment_methods.rs +++ b/crates/router/src/core/payment_methods.rs @@ -266,6 +266,7 @@ pub async fn render_pm_collect_link( // Fetch customer let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &req.merchant_id, &key_store, diff --git a/crates/router/src/core/payment_methods/cards.rs b/crates/router/src/core/payment_methods/cards.rs index 468bc0dbfc3c..431826fbdb0b 100644 --- a/crates/router/src/core/payment_methods/cards.rs +++ b/crates/router/src/core/payment_methods/cards.rs @@ -23,11 +23,15 @@ use common_enums::enums::MerchantStorageScheme; use common_utils::{ consts, crypto::Encryptable, + encryption::Encryption, ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, generate_id, id_type, - types::MinorUnit, + types::{ + keymanager::{Identifier, KeyManagerState}, + MinorUnit, + }, }; -use diesel_models::{business_profile::BusinessProfile, encryption::Encryption, payment_method}; +use diesel_models::{business_profile::BusinessProfile, payment_method}; use domain::CustomerUpdate; use error_stack::{report, ResultExt}; use euclid::{ @@ -77,7 +81,7 @@ use crate::{ #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, req: &api::PaymentMethodCreate, customer_id: &id_type::CustomerId, payment_method_id: &str, @@ -94,8 +98,10 @@ pub async fn create_payment_method( payment_method_billing_address: Option, card_scheme: Option, ) -> errors::CustomResult { + let db = &*state.store; let customer = db .find_customer_by_customer_id_merchant_id( + &state.into(), customer_id, merchant_id, key_store, @@ -153,7 +159,7 @@ pub async fn create_payment_method( if customer.default_payment_method_id.is_none() && req.payment_method.is_some() { let _ = set_default_payment_method( - db, + state, merchant_id.to_string(), key_store.clone(), customer_id, @@ -197,7 +203,7 @@ pub fn store_default_payment_method( } #[instrument(skip_all)] pub async fn get_or_insert_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, req: api::PaymentMethodCreate, resp: &mut api::PaymentMethodResponse, merchant_account: &domain::MerchantAccount, @@ -206,6 +212,7 @@ pub async fn get_or_insert_payment_method( ) -> errors::RouterResult { let mut payment_method_id = resp.payment_method_id.clone(); let mut locker_id = None; + let db = &*state.store; let payment_method = { let existing_pm_by_pmid = db .find_payment_method(&payment_method_id, merchant_account.storage_scheme) @@ -240,7 +247,7 @@ pub async fn get_or_insert_payment_method( Err(err) => { if err.current_context().is_db_not_found() { insert_payment_method( - db, + state, resp, &req, key_store, @@ -278,7 +285,7 @@ pub async fn migrate_payment_method( if let Some(connector_mandate_details) = &req.connector_mandate_details { helpers::validate_merchant_connector_ids_in_connector_mandate_details( - &*state.store, + &state, key_store, connector_mandate_details, merchant_id, @@ -294,7 +301,7 @@ pub async fn migrate_payment_method( &req, ); get_client_secret_or_add_payment_method( - state, + &state, payment_method_create_request, merchant_account, key_store, @@ -338,7 +345,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| create_encrypted_data(key_store, billing)) + .async_map(|billing| create_encrypted_data(&state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -346,6 +353,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &merchant_id, key_store, @@ -475,7 +483,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( let payment_method_data_encrypted: Option>> = payment_method_card_details - .async_map(|card_details| create_encrypted_data(key_store, card_details)) + .async_map(|card_details| create_encrypted_data(&state, key_store, card_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -535,7 +543,7 @@ pub async fn skip_locker_call_and_migrate_payment_method( if customer.default_payment_method_id.is_none() && req.payment_method.is_some() { let _ = set_default_payment_method( - &*state.store, + &state, merchant_id.to_string(), key_store.clone(), &customer_id, @@ -572,12 +580,11 @@ pub fn get_card_bin_and_last4_digits_for_masked_card( #[instrument(skip_all)] pub async fn get_client_secret_or_add_payment_method( - state: routes::SessionState, + state: &routes::SessionState, req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> errors::RouterResponse { - let db = &*state.store; let merchant_id = &merchant_account.merchant_id; let customer_id = req.customer_id.clone().get_required_value("customer_id")?; @@ -589,7 +596,7 @@ pub async fn get_client_secret_or_add_payment_method( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| create_encrypted_data(key_store, billing)) + .async_map(|billing| create_encrypted_data(state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -608,7 +615,7 @@ pub async fn get_client_secret_or_add_payment_method( let payment_method_id = generate_id(consts::ID_LENGTH, "pm"); let res = create_payment_method( - db, + state, &req, &customer_id, payment_method_id.as_str(), @@ -629,7 +636,7 @@ pub async fn get_client_secret_or_add_payment_method( if res.status == enums::PaymentMethodStatus::AwaitingData { add_payment_method_status_update_task( - db, + &*state.store, &res, enums::PaymentMethodStatus::AwaitingData, enums::PaymentMethodStatus::Inactive, @@ -708,6 +715,7 @@ pub async fn add_payment_method_data( let customer_id = payment_method.customer_id.clone(); let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &merchant_account.merchant_id, &key_store, @@ -754,7 +762,7 @@ pub async fn add_payment_method_data( .attach_printable("Failed to add payment method in db")?; get_or_insert_payment_method( - db, + &state, req.clone(), &mut pm_resp, &merchant_account, @@ -795,6 +803,7 @@ pub async fn add_payment_method_data( let pm_data_encrypted: Encryptable> = create_encrypted_data( + &state, &key_store, PaymentMethodsData::Card(updated_card), ) @@ -822,7 +831,7 @@ pub async fn add_payment_method_data( if customer.default_payment_method_id.is_none() { let _ = set_default_payment_method( - db, + &state, merchant_account.merchant_id.clone(), key_store.clone(), &customer_id, @@ -864,7 +873,7 @@ pub async fn add_payment_method_data( #[instrument(skip_all)] pub async fn add_payment_method( - state: routes::SessionState, + state: &routes::SessionState, req: api::PaymentMethodCreate, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -877,7 +886,7 @@ pub async fn add_payment_method( let payment_method_billing_address: Option>> = req .billing .clone() - .async_map(|billing| create_encrypted_data(key_store, billing)) + .async_map(|billing| create_encrypted_data(state, key_store, billing)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -894,7 +903,7 @@ pub async fn add_payment_method( #[cfg(feature = "payouts")] api_enums::PaymentMethod::BankTransfer => match req.bank_transfer.clone() { Some(bank) => add_bank_to_locker( - &state, + state, req.clone(), merchant_account, key_store, @@ -923,7 +932,7 @@ pub async fn add_payment_method( &card_details.card_exp_year, )?; Box::pin(add_card_to_locker( - &state, + state, req.clone(), &card_details, &customer_id, @@ -953,7 +962,7 @@ pub async fn add_payment_method( Some(duplication_check) => match duplication_check { payment_methods::DataDuplicationCheck::Duplicated => { let existing_pm = get_or_insert_payment_method( - db, + state, req.clone(), &mut resp, merchant_account, @@ -967,7 +976,7 @@ pub async fn add_payment_method( payment_methods::DataDuplicationCheck::MetaDataChanged => { if let Some(card) = req.card.clone() { let existing_pm = get_or_insert_payment_method( - db, + state, req.clone(), &mut resp, merchant_account, @@ -979,7 +988,7 @@ pub async fn add_payment_method( let client_secret = existing_pm.client_secret.clone(); delete_card_from_locker( - &state, + state, &customer_id, merchant_id, existing_pm @@ -990,7 +999,7 @@ pub async fn add_payment_method( .await?; let add_card_resp = add_card_hs( - &state, + state, req.clone(), &card, &customer_id, @@ -1018,12 +1027,9 @@ pub async fn add_payment_method( .attach_printable("Failed while updating card metadata changes"))? }; - let existing_pm_data = get_card_details_without_locker_fallback( - &existing_pm, - key_store.key.peek(), - &state, - ) - .await?; + let existing_pm_data = + get_card_details_without_locker_fallback(&existing_pm, state, key_store) + .await?; let updated_card = Some(api::CardDetailFromLocker { scheme: existing_pm.scheme.clone(), @@ -1052,7 +1058,9 @@ pub async fn add_payment_method( }); let pm_data_encrypted: Option>> = updated_pmd - .async_map(|updated_pmd| create_encrypted_data(key_store, updated_pmd)) + .async_map(|updated_pmd| { + create_encrypted_data(state, key_store, updated_pmd) + }) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1087,7 +1095,7 @@ pub async fn add_payment_method( }; resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm"); let pm = insert_payment_method( - db, + state, &resp, &req, key_store, @@ -1112,7 +1120,7 @@ pub async fn add_payment_method( #[allow(clippy::too_many_arguments)] pub async fn insert_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, resp: &api::PaymentMethodResponse, req: &api::PaymentMethodCreate, key_store: &domain::MerchantKeyStore, @@ -1133,14 +1141,14 @@ pub async fn insert_payment_method( let pm_data_encrypted: Option>> = pm_card_details .clone() - .async_map(|pm_card| create_encrypted_data(key_store, pm_card)) + .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt payment method data")?; create_payment_method( - db, + state, req, customer_id, &resp.payment_method_id, @@ -1202,7 +1210,9 @@ pub async fn update_customer_payment_method( // Fetch the existing payment method data from db let existing_card_data = decrypt::( + &(&state).into(), pm.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -1327,7 +1337,7 @@ pub async fn update_customer_payment_method( .map(|card| PaymentMethodsData::Card(CardDetailsPaymentMethod::from(card.clone()))); let pm_data_encrypted: Option>> = updated_pmd - .async_map(|updated_pmd| create_encrypted_data(&key_store, updated_pmd)) + .async_map(|updated_pmd| create_encrypted_data(&state, &key_store, updated_pmd)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1445,6 +1455,7 @@ pub async fn add_bank_to_locker( > { let key = key_store.key.get_inner().peek(); let payout_method_data = api::PayoutMethodData::Bank(bank.clone()); + let key_manager_state: KeyManagerState = state.into(); let enc_data = async { serde_json::to_value(payout_method_data.to_owned()) .map_err(|err| { @@ -1458,7 +1469,14 @@ pub async fn add_bank_to_locker( let secret: Secret = Secret::new(v.to_string()); secret }) - .async_lift(|inner| domain::types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain::types::encrypt_optional( + &key_manager_state, + inner, + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + }) .await } .await @@ -1654,6 +1672,7 @@ pub async fn add_card_hs( #[instrument(skip_all)] pub async fn decode_and_decrypt_locker_data( + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, enc_card_data: String, ) -> errors::CustomResult, errors::VaultError> { @@ -1664,13 +1683,18 @@ pub async fn decode_and_decrypt_locker_data( .change_context(errors::VaultError::ResponseDeserializationFailed) .attach_printable("Failed to decode hex string into bytes")?; // Decrypt - decrypt(Some(Encryption::new(decoded_bytes.into())), key) - .await - .change_context(errors::VaultError::FetchPaymentMethodFailed)? - .map_or( - Err(report!(errors::VaultError::FetchPaymentMethodFailed)), - |d| Ok(d.into_inner()), - ) + decrypt( + &state.into(), + Some(Encryption::new(decoded_bytes.into())), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .change_context(errors::VaultError::FetchPaymentMethodFailed)? + .map_or( + Err(report!(errors::VaultError::FetchPaymentMethodFailed)), + |d| Ok(d.into_inner()), + ) } #[instrument(skip_all)] @@ -1729,9 +1753,9 @@ pub async fn get_payment_method_from_hs_locker<'a>( .attach_printable( "Failed to retrieve field - enc_card_data from RetrieveCardRespPayload", )?; - decode_and_decrypt_locker_data(key_store, enc_card_data.peek().to_string()).await? + decode_and_decrypt_locker_data(state, key_store, enc_card_data.peek().to_string()).await? } else { - mock_get_payment_method(&*state.store, key_store, payment_method_reference) + mock_get_payment_method(state, key_store, payment_method_reference) .await? .payment_method .payment_method_data @@ -2036,16 +2060,17 @@ pub async fn mock_get_card<'a>( #[instrument(skip_all)] pub async fn mock_get_payment_method<'a>( - db: &dyn db::StorageInterface, + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, card_id: &'a str, ) -> errors::CustomResult { + let db = &*state.store; let locker_mock_up = db .find_locker_by_card_id(card_id) .await .change_context(errors::VaultError::FetchPaymentMethodFailed)?; let dec_data = if let Some(e) = locker_mock_up.enc_card_data { - decode_and_decrypt_locker_data(key_store, e).await + decode_and_decrypt_locker_data(state, key_store, e).await } else { Err(report!(errors::VaultError::FetchPaymentMethodFailed)) }?; @@ -2187,14 +2212,14 @@ pub async fn list_payment_methods( ) -> errors::RouterResponse { let db = &*state.store; let pm_config_mapping = &state.conf.pm_filters; - + let key_manager_state = &(&state).into(); let payment_intent = if let Some(cs) = &req.client_secret { if cs.starts_with("pm_") { validate_payment_method_and_client_secret(cs, db, &merchant_account).await?; None } else { helpers::verify_payment_intent_time_and_client_secret( - db, + &state, &merchant_account, &key_store, req.client_secret.clone(), @@ -2209,7 +2234,7 @@ pub async fn list_payment_methods( .as_ref() .async_map(|pi| async { helpers::get_address_by_id( - db, + &state, pi.shipping_address_id.clone(), &key_store, &pi.payment_id, @@ -2226,7 +2251,7 @@ pub async fn list_payment_methods( .as_ref() .async_map(|pi| async { helpers::get_address_by_id( - db, + &state, pi.billing_address_id.clone(), &key_store, &pi.payment_id, @@ -2246,6 +2271,7 @@ pub async fn list_payment_methods( .as_ref() .async_and_then(|cust| async { db.find_customer_by_customer_id_merchant_id( + key_manager_state, cust, &pi.merchant_id, &key_store, @@ -2293,6 +2319,7 @@ pub async fn list_payment_methods( let all_mcas = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, &merchant_account.merchant_id, false, &key_store, @@ -3273,6 +3300,7 @@ pub async fn call_surcharge_decision_management( let _ = state .store .update_payment_intent( + &(&state).into(), payment_intent, storage::PaymentIntentUpdate::SurchargeApplicableUpdate { surcharge_applicable: true, @@ -3322,6 +3350,7 @@ pub async fn call_surcharge_decision_management_for_saved_card( let _ = state .store .update_payment_intent( + &state.into(), payment_intent, storage::PaymentIntentUpdate::SurchargeApplicableUpdate { surcharge_applicable: true, @@ -3586,7 +3615,6 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( customer_id: Option<&id_type::CustomerId>, ephemeral_api_key: Option<&str>, ) -> errors::RouterResponse { - let db = state.store.as_ref(); let limit = req.clone().and_then(|pml_req| pml_req.limit); let auth_cust = if let Some(key) = ephemeral_api_key { @@ -3617,7 +3645,7 @@ pub async fn do_list_customer_pm_fetch_customer_if_not_passed( let cloned_secret = req.and_then(|r| r.client_secret.as_ref().cloned()); let payment_intent: Option = helpers::verify_payment_intent_time_and_client_secret( - db, + &state, &merchant_account, &key_store, cloned_secret, @@ -3671,6 +3699,7 @@ pub async fn list_customer_payment_method( let customer = db .find_customer_by_customer_id_merchant_id( + &state.into(), customer_id, &merchant_account.merchant_id, &key_store, @@ -3679,8 +3708,6 @@ pub async fn list_customer_payment_method( .await .to_not_found_response(errors::ApiErrorResponse::CustomerNotFound)?; - let key = key_store.key.get_inner().peek(); - let is_requires_cvv = db .find_config_by_key_unwrap_or( format!("{}_requires_cvv", merchant_account.merchant_id).as_str(), @@ -3741,7 +3768,8 @@ pub async fn list_customer_payment_method( let payment_method_retrieval_context = match payment_method { enums::PaymentMethod::Card => { - let card_details = get_card_details_with_locker_fallback(&pm, key, state).await?; + let card_details = + get_card_details_with_locker_fallback(&pm, state, &key_store).await?; if card_details.is_some() { PaymentMethodListContext { @@ -3761,12 +3789,13 @@ pub async fn list_customer_payment_method( enums::PaymentMethod::BankDebit => { // Retrieve the pm_auth connector details so that it can be tokenized - let bank_account_token_data = get_bank_account_connector_details(&pm, key) - .await - .unwrap_or_else(|error| { - logger::error!(?error); - None - }); + let bank_account_token_data = + get_bank_account_connector_details(state, &pm, &key_store) + .await + .unwrap_or_else(|error| { + logger::error!(?error); + None + }); if let Some(data) = bank_account_token_data { let token_data = PaymentTokenData::AuthBankDebit(data); @@ -3822,7 +3851,7 @@ pub async fn list_customer_payment_method( // Retrieve the masked bank details to be sent as a response let bank_details = if payment_method == enums::PaymentMethod::BankDebit { - get_masked_bank_details(&pm, key) + get_masked_bank_details(state, &pm, &key_store) .await .unwrap_or_else(|error| { logger::error!(?error); @@ -3833,8 +3862,9 @@ pub async fn list_customer_payment_method( }; let payment_method_billing = decrypt_generic_data::( + state, pm.payment_method_billing_address, - key, + &key_store, ) .await .attach_printable("unable to decrypt payment method billing address details")?; @@ -3993,6 +4023,7 @@ pub async fn get_mca_status( let mcas = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, @@ -4021,17 +4052,21 @@ pub async fn get_mca_status( Ok(false) } pub async fn decrypt_generic_data( + state: &routes::SessionState, data: Option, - key: &[u8], + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> where T: serde::de::DeserializeOwned, { - let decrypted_data = decrypt::(data, key) - .await - .change_context(errors::StorageError::DecryptionError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to decrypt data")?; + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let decrypted_data = + decrypt::(&state.into(), data, identifier, key) + .await + .change_context(errors::StorageError::DecryptionError) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to decrypt data")?; decrypted_data .map(|decrypted_data| decrypted_data.into_inner().expose()) @@ -4043,22 +4078,28 @@ where pub async fn get_card_details_with_locker_fallback( pm: &payment_method::PaymentMethod, - key: &[u8], state: &routes::SessionState, + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { - let card_decrypted = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt card details") - .ok() - .flatten() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let card_decrypted = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); Ok(if let Some(mut crd) = card_decrypted { crd.scheme.clone_from(&pm.scheme); @@ -4073,22 +4114,28 @@ pub async fn get_card_details_with_locker_fallback( pub async fn get_card_details_without_locker_fallback( pm: &payment_method::PaymentMethod, - key: &[u8], state: &routes::SessionState, + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult { - let card_decrypted = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .attach_printable("unable to decrypt card details") - .ok() - .flatten() - .map(|x| x.into_inner().expose()) - .and_then(|v| serde_json::from_value::(v).ok()) - .and_then(|pmd| match pmd { - PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), - _ => None, - }); + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let card_decrypted = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .attach_printable("unable to decrypt card details") + .ok() + .flatten() + .map(|x| x.into_inner().expose()) + .and_then(|v| serde_json::from_value::(v).ok()) + .and_then(|pmd| match pmd { + PaymentMethodsData::Card(crd) => Some(api::CardDetailFromLocker::from(crd)), + _ => None, + }); Ok(if let Some(mut crd) = card_decrypted { crd.scheme.clone_from(&pm.scheme); @@ -4141,25 +4188,32 @@ pub async fn get_lookup_key_from_locker( } async fn get_masked_bank_details( + state: &routes::SessionState, pm: &payment_method::PaymentMethod, - key: &[u8], + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { - let payment_method_data = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to decrypt bank details")? - .map(|x| x.into_inner().expose()) - .map( - |v| -> Result> { - v.parse_value::("PaymentMethodsData") - .change_context(errors::StorageError::DeserializationFailed) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize Payment Method Auth config") - }, - ) - .transpose()?; + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let payment_method_data = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to decrypt bank details")? + .map(|x| x.into_inner().expose()) + .map( + |v| -> Result> { + v.parse_value::("PaymentMethodsData") + .change_context(errors::StorageError::DeserializationFailed) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize Payment Method Auth config") + }, + ) + .transpose()?; match payment_method_data { Some(pmd) => match pmd { @@ -4174,25 +4228,32 @@ async fn get_masked_bank_details( } async fn get_bank_account_connector_details( + state: &routes::SessionState, pm: &payment_method::PaymentMethod, - key: &[u8], + key_store: &domain::MerchantKeyStore, ) -> errors::RouterResult> { - let payment_method_data = - decrypt::(pm.payment_method_data.clone(), key) - .await - .change_context(errors::StorageError::DecryptionError) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("unable to decrypt bank details")? - .map(|x| x.into_inner().expose()) - .map( - |v| -> Result> { - v.parse_value::("PaymentMethodsData") - .change_context(errors::StorageError::DeserializationFailed) - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed to deserialize Payment Method Auth config") - }, - ) - .transpose()?; + let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let payment_method_data = decrypt::( + &state.into(), + pm.payment_method_data.clone(), + identifier, + key, + ) + .await + .change_context(errors::StorageError::DecryptionError) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("unable to decrypt bank details")? + .map(|x| x.into_inner().expose()) + .map( + |v| -> Result> { + v.parse_value::("PaymentMethodsData") + .change_context(errors::StorageError::DeserializationFailed) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed to deserialize Payment Method Auth config") + }, + ) + .transpose()?; match payment_method_data { Some(pmd) => match pmd { @@ -4229,17 +4290,20 @@ async fn get_bank_account_connector_details( } } pub async fn set_default_payment_method( - db: &dyn db::StorageInterface, + state: &routes::SessionState, merchant_id: String, key_store: domain::MerchantKeyStore, customer_id: &id_type::CustomerId, payment_method_id: String, storage_scheme: MerchantStorageScheme, ) -> errors::RouterResponse { + let db = &*state.store; + let key_manager_state = &state.into(); // check for the customer // TODO: customer need not be checked again here, this function can take an optional customer and check for existence of customer based on the optional value let customer = db .find_customer_by_customer_id_merchant_id( + key_manager_state, customer_id, &merchant_id, &key_store, @@ -4283,6 +4347,7 @@ pub async fn set_default_payment_method( // update the db with the default payment method id let updated_customer_details = db .update_customer_by_customer_id_merchant_id( + key_manager_state, customer_id.to_owned(), merchant_id.to_owned(), customer, @@ -4470,7 +4535,6 @@ pub async fn retrieve_payment_method( .await .to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?; - let key = key_store.key.peek(); let card = if pm.payment_method == Some(enums::PaymentMethod::Card) { let card_detail = if state.conf.locker.locker_enabled { let card = get_card_from_locker( @@ -4486,7 +4550,7 @@ pub async fn retrieve_payment_method( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while getting card details from locker")? } else { - get_card_details_without_locker_fallback(&pm, key, &state).await? + get_card_details_without_locker_fallback(&pm, &state, &key_store).await? }; Some(card_detail) } else { @@ -4521,6 +4585,7 @@ pub async fn delete_payment_method( key_store: domain::MerchantKeyStore, ) -> errors::RouterResponse { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key = db .find_payment_method( pm_id.payment_method_id.as_str(), @@ -4531,6 +4596,7 @@ pub async fn delete_payment_method( let customer = db .find_customer_by_customer_id_merchant_id( + key_manager_state, &key.customer_id, &merchant_account.merchant_id, &key_store, @@ -4570,6 +4636,7 @@ pub async fn delete_payment_method( }; db.update_customer_by_customer_id_merchant_id( + key_manager_state, key.customer_id, key.merchant_id, customer, @@ -4591,6 +4658,7 @@ pub async fn delete_payment_method( } pub async fn create_encrypted_data( + state: &routes::SessionState, key_store: &domain::MerchantKeyStore, data: T, ) -> Result>, error_stack::Report> @@ -4598,6 +4666,8 @@ where T: Debug + serde::Serialize, { let key = key_store.key.get_inner().peek(); + let identifier = Identifier::Merchant(key_store.merchant_id.clone()); + let key_manager_state: KeyManagerState = state.into(); let encoded_data = Encode::encode_to_value(&data) .change_context(errors::StorageError::SerializationFailed) @@ -4605,10 +4675,11 @@ where let secret_data = Secret::<_, masking::WithType>::new(encoded_data); - let encrypted_data = domain::types::encrypt(secret_data, key) - .await - .change_context(errors::StorageError::EncryptionError) - .attach_printable("Unable to encrypt data")?; + let encrypted_data = + domain::types::encrypt(&key_manager_state, secret_data, identifier.clone(), key) + .await + .change_context(errors::StorageError::EncryptionError) + .attach_printable("Unable to encrypt data")?; Ok(encrypted_data) } diff --git a/crates/router/src/core/payment_methods/validator.rs b/crates/router/src/core/payment_methods/validator.rs index be34d3db38c8..23470fb3ef17 100644 --- a/crates/router/src/core/payment_methods/validator.rs +++ b/crates/router/src/core/payment_methods/validator.rs @@ -27,6 +27,7 @@ pub async fn validate_request_and_initiate_payment_method_collect_link( let merchant_id = merchant_account.merchant_id.clone(); match db .find_customer_by_customer_id_merchant_id( + &state.into(), &customer_id, &merchant_id, key_store, diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs index 5cad096bd395..9c420c1e44da 100644 --- a/crates/router/src/core/payments.rs +++ b/crates/router/src/core/payments.rs @@ -164,7 +164,7 @@ where let (operation, customer) = operation .to_domain()? .get_or_create_customer_details( - &*state.store, + state, &mut payment_data, customer_details, &key_store, @@ -208,8 +208,6 @@ where // Fetch and check FRM configs #[cfg(feature = "frm")] let mut frm_info = None; - #[cfg(feature = "frm")] - let db = &*state.store; #[allow(unused_variables, unused_mut)] let mut should_continue_transaction: bool = true; #[cfg(feature = "frm")] @@ -217,7 +215,6 @@ where #[cfg(feature = "frm")] let frm_configs = if state.conf.frm.enabled { Box::pin(frm_core::call_frm_before_connector_call( - db, &operation, &merchant_account, &mut payment_data, @@ -1203,6 +1200,7 @@ impl PaymentRedirectFlow for PaymentAuthenticateCompleteAuthorize { let payment_intent = state .store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, &merchant_id, &merchant_key_store, @@ -2883,7 +2881,7 @@ pub async fn list_payments( let merchant_id = &merchant.merchant_id; let db = state.store.as_ref(); let payment_intents = helpers::filter_by_constraints( - db, + &state, &constraints, merchant_id, &key_store, @@ -2955,6 +2953,7 @@ pub async fn apply_filters_on_payments( let db = state.store.as_ref(); let list: Vec<(storage::PaymentIntent, storage::PaymentAttempt)> = db .get_filtered_payment_intents_attempt( + &(&state).into(), &merchant.merchant_id, &constraints.clone().into(), &merchant_key_store, @@ -3008,6 +3007,7 @@ pub async fn get_filters_for_payments( let db = state.store.as_ref(); let pi = db .filter_payment_intents_by_time_range_constraints( + &(&state).into(), &merchant.merchant_id, &time_range, &merchant_key_store, @@ -4021,6 +4021,7 @@ pub async fn payment_external_authentication( let payment_id = req.payment_id; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, merchant_id, &key_store, @@ -4054,6 +4055,7 @@ pub async fn payment_external_authentication( state .store .find_customer_by_customer_id_merchant_id( + &(&state).into(), customer_id, &merchant_account.merchant_id, &key_store, @@ -4076,7 +4078,7 @@ pub async fn payment_external_authentication( let currency = payment_attempt.currency.get_required_value("currency")?; let amount = payment_attempt.get_total_amount(); let shipping_address = helpers::create_or_find_address_for_payment_by_request( - db, + &state, None, payment_intent.shipping_address_id.as_deref(), merchant_id, @@ -4087,7 +4089,7 @@ pub async fn payment_external_authentication( ) .await?; let billing_address = helpers::create_or_find_address_for_payment_by_request( - db, + &state, None, payment_intent.billing_address_id.as_deref(), merchant_id, @@ -4259,9 +4261,11 @@ pub async fn payments_manual_update( error_message, error_reason, } = req; + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -4270,7 +4274,7 @@ pub async fn payments_manual_update( .attach_printable("Error while fetching the key store by merchant_id")?; let merchant_account = state .store - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) .attach_printable("Error while fetching the merchant_account by merchant_id")?; @@ -4290,6 +4294,7 @@ pub async fn payments_manual_update( let payment_intent = state .store .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payment_id, &merchant_account.merchant_id, &key_store, @@ -4345,6 +4350,7 @@ pub async fn payments_manual_update( state .store .update_payment_intent( + &(&state).into(), payment_intent, payment_intent_update, &key_store, diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs index 3629457d5ab6..ecf8ff0c40d2 100644 --- a/crates/router/src/core/payments/helpers.rs +++ b/crates/router/src/core/payments/helpers.rs @@ -1,8 +1,9 @@ use std::{borrow::Cow, str::FromStr}; use api_models::{ + customers::CustomerRequestWithEmail, mandates::RecurringDetails, - payments::{CardToken, GetPaymentMethodType, RequestSurchargeDetails}, + payments::{AddressDetailsWithPhone, CardToken, GetPaymentMethodType, RequestSurchargeDetails}, }; use base64::Engine; use common_enums::ConnectorType; @@ -10,7 +11,10 @@ use common_utils::{ crypto::Encryptable, ext_traits::{AsyncExt, ByteSliceExt, Encode, ValueExt}, fp_utils, generate_id, id_type, pii, - types::MinorUnit, + types::{ + keymanager::{Identifier, KeyManagerState, ToEncryptable}, + MinorUnit, + }, }; use diesel_models::enums::{self}; // TODO : Evaluate all the helper functions () @@ -139,7 +143,7 @@ pub fn filter_mca_based_on_connector_type( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_or_update_address_for_payment_by_request( - db: &dyn StorageInterface, + session_state: &SessionState, req_address: Option<&api::Address>, address_id: Option<&str>, merchant_id: &str, @@ -149,87 +153,54 @@ pub async fn create_or_update_address_for_payment_by_request( storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, errors::ApiErrorResponse> { let key = merchant_key_store.key.get_inner().peek(); - + let db = &session_state.store; + let key_manager_state = &session_state.into(); Ok(match address_id { Some(id) => match req_address { Some(address) => { - let address_update = async { - Ok::<_, error_stack::Report>( - storage::AddressUpdate::Update { - city: address - .address - .as_ref() - .and_then(|value| value.city.clone()), - country: address.address.as_ref().and_then(|value| value.country), - line1: address - .address - .as_ref() - .and_then(|value| value.line1.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line2: address - .address - .as_ref() - .and_then(|value| value.line2.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line3: address - .address - .as_ref() - .and_then(|value| value.line3.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - state: address - .address - .as_ref() - .and_then(|value| value.state.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - zip: address - .address - .as_ref() - .and_then(|value| value.zip.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - first_name: address - .address - .as_ref() - .and_then(|value| value.first_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - last_name: address - .address - .as_ref() - .and_then(|value| value.last_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - phone_number: address - .phone - .as_ref() - .and_then(|value| value.number.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - country_code: address - .phone - .as_ref() - .and_then(|value| value.country_code.clone()), - updated_by: storage_scheme.to_string(), - email: address - .email - .as_ref() - .cloned() - .async_lift(|inner| { - types::encrypt_optional(inner.map(|inner| inner.expose()), key) - }) - .await?, - }, - ) - } + let encrypted_data = types::batch_encrypt( + &session_state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: address.address.clone(), + phone_number: address + .phone + .as_ref() + .and_then(|phone| phone.number.clone()), + email: address.email.clone(), + }), + Identifier::Merchant(merchant_key_store.merchant_id.clone()), + key, + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encrypting address")?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encrypting address")?; + let address_update = storage::AddressUpdate::Update { + city: address + .address + .as_ref() + .and_then(|value| value.city.clone()), + country: address.address.as_ref().and_then(|value| value.country), + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + state: encryptable_address.state, + zip: encryptable_address.zip, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: address + .phone + .as_ref() + .and_then(|value| value.country_code.clone()), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }; let address = db .find_address_by_merchant_id_payment_id_address_id( + key_manager_state, merchant_id, payment_id, id, @@ -241,6 +212,7 @@ pub async fn create_or_update_address_for_payment_by_request( .attach_printable("Error while fetching address")?; Some( db.update_address_for_payments( + key_manager_state, address, address_update, payment_id.to_string(), @@ -254,6 +226,7 @@ pub async fn create_or_update_address_for_payment_by_request( } None => Some( db.find_address_by_merchant_id_payment_id_address_id( + key_manager_state, merchant_id, payment_id, id, @@ -268,10 +241,11 @@ pub async fn create_or_update_address_for_payment_by_request( }, None => match req_address { Some(address) => { - let address = get_domain_address(address, merchant_id, key, storage_scheme) - .await - .change_context(errors::ApiErrorResponse::InternalServerError) - .attach_printable("Failed while encrypting address while insert")?; + let address = + get_domain_address(session_state, address, merchant_id, key, storage_scheme) + .await + .change_context(errors::ApiErrorResponse::InternalServerError) + .attach_printable("Failed while encrypting address while insert")?; let payment_address = domain::PaymentAddress { address, @@ -281,6 +255,7 @@ pub async fn create_or_update_address_for_payment_by_request( Some( db.insert_address_for_payments( + key_manager_state, payment_id, payment_address, merchant_key_store, @@ -301,7 +276,7 @@ pub async fn create_or_update_address_for_payment_by_request( #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] pub async fn create_or_find_address_for_payment_by_request( - db: &dyn StorageInterface, + state: &SessionState, req_address: Option<&api::Address>, address_id: Option<&str>, merchant_id: &str, @@ -311,10 +286,12 @@ pub async fn create_or_find_address_for_payment_by_request( storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, errors::ApiErrorResponse> { let key = merchant_key_store.key.get_inner().peek(); - + let db = &state.store; + let key_manager_state = &state.into(); Ok(match address_id { Some(id) => Some( db.find_address_by_merchant_id_payment_id_address_id( + key_manager_state, merchant_id, payment_id, id, @@ -329,7 +306,7 @@ pub async fn create_or_find_address_for_payment_by_request( None => match req_address { Some(address) => { // generate a new address here - let address = get_domain_address(address, merchant_id, key, storage_scheme) + let address = get_domain_address(state, address, merchant_id, key, storage_scheme) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed while encrypting address while insert")?; @@ -342,6 +319,7 @@ pub async fn create_or_find_address_for_payment_by_request( Some( db.insert_address_for_payments( + key_manager_state, payment_id, payment_address, merchant_key_store, @@ -359,70 +337,56 @@ pub async fn create_or_find_address_for_payment_by_request( } pub async fn get_domain_address( + session_state: &SessionState, address: &api_models::payments::Address, merchant_id: &str, key: &[u8], storage_scheme: enums::MerchantStorageScheme, ) -> CustomResult { async { - let address_details = address.address.as_ref(); + let address_details = &address.address.as_ref(); + let encrypted_data = types::batch_encrypt( + &session_state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: address_details.cloned(), + phone_number: address + .phone + .as_ref() + .and_then(|phone| phone.number.clone()), + email: address.email.clone(), + }), + Identifier::Merchant(merchant_id.to_string()), + key, + ) + .await?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; Ok(domain::Address { id: None, - phone_number: address - .phone - .as_ref() - .and_then(|a| a.number.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + phone_number: encryptable_address.phone_number, country_code: address.phone.as_ref().and_then(|a| a.country_code.clone()), merchant_id: merchant_id.to_string(), address_id: generate_id(consts::ID_LENGTH, "add"), city: address_details.and_then(|address_details| address_details.city.clone()), country: address_details.and_then(|address_details| address_details.country), - line1: address_details - .and_then(|address_details| address_details.line1.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line2: address_details - .and_then(|address_details| address_details.line2.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - line3: address_details - .and_then(|address_details| address_details.line3.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - state: address_details - .and_then(|address_details| address_details.state.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + state: encryptable_address.state, created_at: common_utils::date_time::now(), - first_name: address_details - .and_then(|address_details| address_details.first_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - last_name: address_details - .and_then(|address_details| address_details.last_name.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, modified_at: common_utils::date_time::now(), - zip: address_details - .and_then(|address_details| address_details.zip.clone()) - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, + zip: encryptable_address.zip, updated_by: storage_scheme.to_string(), - email: address - .email - .as_ref() - .cloned() - .async_lift(|inner| types::encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, + email: encryptable_address.email, }) } .await } pub async fn get_address_by_id( - db: &dyn StorageInterface, + state: &SessionState, address_id: Option, merchant_key_store: &domain::MerchantKeyStore, payment_id: &str, @@ -431,17 +395,21 @@ pub async fn get_address_by_id( ) -> CustomResult, errors::ApiErrorResponse> { match address_id { None => Ok(None), - Some(address_id) => Ok(db - .find_address_by_merchant_id_payment_id_address_id( - merchant_id, - payment_id, - &address_id, - merchant_key_store, - storage_scheme, - ) - .await - .map(|payment_address| payment_address.address) - .ok()), + Some(address_id) => { + let db = &*state.store; + Ok(db + .find_address_by_merchant_id_payment_id_address_id( + &state.into(), + merchant_id, + payment_id, + &address_id, + merchant_key_store, + storage_scheme, + ) + .await + .map(|payment_address| payment_address.address) + .ok()) + } } } @@ -688,12 +656,13 @@ pub async fn get_token_for_recurring_mandate( ) .await .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?; - + let key_manager_state: KeyManagerState = state.into(); let original_payment_intent = mandate .original_payment_id .as_ref() .async_map(|payment_id| async { db.find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, payment_id, &mandate.merchant_id, merchant_key_store, @@ -1429,7 +1398,7 @@ pub(crate) async fn get_payment_method_create_request( } pub async fn get_customer_from_details( - db: &dyn StorageInterface, + state: &SessionState, customer_id: Option, merchant_id: &str, payment_data: &mut PaymentData, @@ -1439,8 +1408,10 @@ pub async fn get_customer_from_details( match customer_id { None => Ok(None), Some(customer_id) => { + let db = &*state.store; let customer = db .find_customer_optional_by_customer_id_merchant_id( + &state.into(), &customer_id, merchant_id, merchant_key_store, @@ -1549,8 +1520,8 @@ pub async fn get_connector_default( #[instrument(skip_all)] #[allow(clippy::type_complexity)] pub async fn create_customer_if_not_exist<'a, F: Clone, R>( + state: &SessionState, operation: BoxedOperation<'a, F, R>, - db: &dyn StorageInterface, payment_data: &mut PaymentData, req: Option, merchant_id: &str, @@ -1613,7 +1584,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( payment_data.payment_intent.customer_details = raw_customer_details .clone() - .async_map(|customer_details| create_encrypted_data(key_store, customer_details)) + .async_map(|customer_details| create_encrypted_data(state, key_store, customer_details)) .await .transpose() .change_context(errors::StorageError::EncryptionError) @@ -1622,18 +1593,36 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( let customer_id = request_customer_details .customer_id .or(payment_data.payment_intent.customer_id.clone()); - + let db = &*state.store; + let key_manager_state = &state.into(); let optional_customer = match customer_id { Some(customer_id) => { let customer_data = db .find_customer_optional_by_customer_id_merchant_id( + key_manager_state, &customer_id, merchant_id, key_store, storage_scheme, ) .await?; - + let key = key_store.key.get_inner().peek(); + let encrypted_data = types::batch_encrypt( + key_manager_state, + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: request_customer_details.name.clone(), + email: request_customer_details.email.clone(), + phone: request_customer_details.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .change_context(errors::StorageError::SerializationFailed) + .attach_printable("Failed while encrypting Customer while Update")?; + let encryptable_customer = CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::StorageError::SerializationFailed) + .attach_printable("Failed while encrypting Customer while Update")?; Some(match customer_data { Some(c) => { // Update the customer data if new data is passed in the request @@ -1642,44 +1631,19 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( | request_customer_details.phone.is_some() | request_customer_details.phone_country_code.is_some() { - let key = key_store.key.get_inner().peek(); - let customer_update = async { - Ok::<_, error_stack::Report>( - Update { - name: request_customer_details - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: request_customer_details - .email - .clone() - .async_lift(|inner| { - types::encrypt_optional( - inner.map(|inner| inner.expose()), - key, - ) - }) - .await?, - phone: Box::new( - request_customer_details - .phone - .clone() - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - ), - phone_country_code: request_customer_details.phone_country_code, - description: None, - connector_customer: None, - metadata: None, - address_id: None, - }, - ) - } - .await - .change_context(errors::StorageError::SerializationFailed) - .attach_printable("Failed while encrypting Customer while Update")?; + let customer_update = Update { + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: Box::new(encryptable_customer.phone), + phone_country_code: request_customer_details.phone_country_code, + description: None, + connector_customer: None, + metadata: None, + address_id: None, + }; db.update_customer_by_customer_id_merchant_id( + key_manager_state, customer_id, merchant_id.to_string(), c, @@ -1693,51 +1657,25 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( } } None => { - let new_customer = async { - let key = key_store.key.get_inner().peek(); - Ok::<_, error_stack::Report>( - domain::Customer { - customer_id, - merchant_id: merchant_id.to_string(), - name: request_customer_details - .name - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - email: request_customer_details - .email - .clone() - .async_lift(|inner| { - types::encrypt_optional( - inner.map(|inner| inner.expose()), - key, - ) - }) - .await?, - phone: request_customer_details - .phone - .clone() - .async_lift(|inner| types::encrypt_optional(inner, key)) - .await?, - phone_country_code: request_customer_details - .phone_country_code - .clone(), - description: None, - created_at: common_utils::date_time::now(), - id: None, - metadata: None, - modified_at: common_utils::date_time::now(), - connector_customer: None, - address_id: None, - default_payment_method_id: None, - updated_by: None, - }, - ) - } - .await - .change_context(errors::StorageError::SerializationFailed) - .attach_printable("Failed while encrypting Customer while insert")?; + let new_customer = domain::Customer { + customer_id, + merchant_id: merchant_id.to_string(), + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, + phone_country_code: request_customer_details.phone_country_code.clone(), + description: None, + created_at: common_utils::date_time::now(), + id: None, + metadata: None, + modified_at: common_utils::date_time::now(), + connector_customer: None, + address_id: None, + default_payment_method_id: None, + updated_by: None, + }; metrics::CUSTOMER_CREATED.add(&metrics::CONTEXT, 1, &[]); - db.insert_customer(new_customer, key_store, storage_scheme) + db.insert_customer(key_manager_state, new_customer, key_store, storage_scheme) .await } }) @@ -1746,6 +1684,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>( None => None, Some(customer_id) => db .find_customer_optional_by_customer_id_merchant_id( + key_manager_state, customer_id, merchant_id, key_store, @@ -2533,14 +2472,16 @@ where #[cfg(feature = "olap")] pub(super) async fn filter_by_constraints( - db: &dyn StorageInterface, + state: &SessionState, constraints: &api::PaymentListConstraints, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: storage_enums::MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { + let db = &*state.store; let result = db .filter_payment_intent_by_constraints( + &(state).into(), merchant_id, &constraints.clone().into(), key_store, @@ -2957,17 +2898,19 @@ pub(crate) fn validate_pm_or_token_given( // A function to perform database lookup and then verify the client secret pub async fn verify_payment_intent_time_and_client_secret( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, client_secret: Option, ) -> error_stack::Result, errors::ApiErrorResponse> { + let db = &*state.store; client_secret .async_map(|cs| async move { let payment_id = get_payment_id_from_client_secret(&cs)?; let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, &merchant_account.merchant_id, key_store, @@ -3371,6 +3314,7 @@ pub async fn get_merchant_connector_account( merchant_connector_id: Option<&String>, ) -> RouterResult { let db = &*state.store; + let key_manager_state = &state.into(); match creds_identifier { Some(creds_identifier) => { let key = format!("mcd_{merchant_id}_{creds_identifier}"); @@ -3445,6 +3389,7 @@ pub async fn get_merchant_connector_account( None => { if let Some(merchant_connector_id) = merchant_connector_id { db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id, merchant_connector_id, key_store, @@ -3457,6 +3402,7 @@ pub async fn get_merchant_connector_account( ) } else { db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, profile_id, connector_name, key_store, @@ -3737,13 +3683,14 @@ impl AttemptType { request: &api::PaymentsRequest, fetched_payment_intent: PaymentIntent, fetched_payment_attempt: PaymentAttempt, - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, storage_scheme: storage::enums::MerchantStorageScheme, ) -> RouterResult<(PaymentIntent, PaymentAttempt)> { match self { Self::SameOld => Ok((fetched_payment_intent, fetched_payment_attempt)), Self::New => { + let db = &*state.store; let new_attempt_count = fetched_payment_intent.attempt_count + 1; let new_payment_attempt = db .insert_payment_attempt( @@ -3766,6 +3713,7 @@ impl AttemptType { let updated_payment_intent = db .update_payment_intent( + &state.into(), fetched_payment_intent, storage::PaymentIntentUpdate::StatusAndAttemptUpdate { status: payment_intent_status_fsm( @@ -4159,6 +4107,7 @@ pub fn is_apple_pay_simplified_flow( } pub async fn get_encrypted_apple_pay_connector_wallets_details( + state: &SessionState, key_store: &domain::MerchantKeyStore, connector_metadata: &Option>, ) -> RouterResult>>> { @@ -4179,10 +4128,15 @@ pub async fn get_encrypted_apple_pay_connector_wallets_details( }) .transpose()? .map(masking::Secret::new); - + let key_manager_state: KeyManagerState = state.into(); let encrypted_connector_apple_pay_details = connector_apple_pay_details .async_lift(|wallets_details| { - types::encrypt_optional(wallets_details, key_store.key.get_inner().peek()) + types::encrypt_optional( + &key_manager_state, + wallets_details, + Identifier::Merchant(key_store.merchant_id.clone()), + key_store.key.get_inner().peek(), + ) }) .await .change_context(errors::ApiErrorResponse::InternalServerError) @@ -4266,6 +4220,7 @@ where let merchant_connector_account_list = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_account.merchant_id.as_str(), false, key_store, @@ -5126,13 +5081,15 @@ pub async fn override_setup_future_usage_to_on_session( } pub async fn validate_merchant_connector_ids_in_connector_mandate_details( - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, connector_mandate_details: &api_models::payment_methods::PaymentsMandateReference, merchant_id: &str, ) -> CustomResult<(), errors::ApiErrorResponse> { + let db = &*state.store; let merchant_connector_account_list = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, diff --git a/crates/router/src/core/payments/operations.rs b/crates/router/src/core/payments/operations.rs index be54ead0ac12..70cc77aa5369 100644 --- a/crates/router/src/core/payments/operations.rs +++ b/crates/router/src/core/payments/operations.rs @@ -28,7 +28,6 @@ pub use self::{ use super::{helpers, CustomerDetails, PaymentData}; use crate::{ core::errors::{self, CustomResult, RouterResult}, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -117,7 +116,7 @@ pub trait Domain: Send + Sync { /// This will fetch customer details, (this operation is flow specific) async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -259,7 +258,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, _request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -274,7 +273,7 @@ where Ok(( Box::new(self), helpers::get_customer_from_details( - db, + state, payment_data.payment_intent.customer_id.clone(), &merchant_key_store.merchant_id, payment_data, @@ -334,7 +333,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, _request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -349,7 +348,7 @@ where Ok(( Box::new(self), helpers::get_customer_from_details( - db, + state, payment_data.payment_intent.customer_id.clone(), &merchant_key_store.merchant_id, payment_data, @@ -408,7 +407,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, _request: Option, merchant_key_store: &domain::MerchantKeyStore, @@ -423,7 +422,7 @@ where Ok(( Box::new(self), helpers::get_customer_from_details( - db, + state, payment_data.payment_intent.customer_id.clone(), &merchant_key_store.merchant_id, payment_data, @@ -483,7 +482,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - _db: &dyn StorageInterface, + _state: &SessionState, _payment_data: &mut PaymentData, _request: Option, _merchant_key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments/operations/payment_approve.rs b/crates/router/src/core/payments/operations/payment_approve.rs index 172c5943c0c9..5b18512fe201 100644 --- a/crates/router/src/core/payments/operations/payment_approve.rs +++ b/crates/router/src/core/payments/operations/payment_approve.rs @@ -53,6 +53,7 @@ impl GetTracker, api::PaymentsCaptureRequest> payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -97,7 +98,7 @@ impl GetTracker, api::PaymentsCaptureRequest> amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -107,7 +108,7 @@ impl GetTracker, api::PaymentsCaptureRequest> .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -117,7 +118,7 @@ impl GetTracker, api::PaymentsCaptureRequest> .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -199,7 +200,7 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: PaymentData, _customer: Option, @@ -224,9 +225,10 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for merchant_decision: Some(api_models::enums::MerchantDecision::Approved.to_string()), updated_by: storage_scheme.to_string(), }; - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, intent_status_update, key_store, @@ -234,7 +236,8 @@ impl UpdateTracker, api::PaymentsCaptureRequest> for ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - db.store + state + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), storage::PaymentAttemptUpdate::StatusUpdate { diff --git a/crates/router/src/core/payments/operations/payment_cancel.rs b/crates/router/src/core/payments/operations/payment_cancel.rs index 94591d44e4bb..a8a2756a58d4 100644 --- a/crates/router/src/core/payments/operations/payment_cancel.rs +++ b/crates/router/src/core/payments/operations/payment_cancel.rs @@ -51,6 +51,7 @@ impl GetTracker, api::PaymentsCancelRequest> let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -82,7 +83,7 @@ impl GetTracker, api::PaymentsCancelRequest> .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -92,7 +93,7 @@ impl GetTracker, api::PaymentsCancelRequest> .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -102,7 +103,7 @@ impl GetTracker, api::PaymentsCancelRequest> .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -212,7 +213,7 @@ impl UpdateTracker, api::PaymentsCancelRequest> for #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, req_state: ReqState, mut payment_data: PaymentData, _customer: Option, @@ -242,9 +243,10 @@ impl UpdateTracker, api::PaymentsCancelRequest> for }; if let Some(payment_intent_update) = intent_status_update { - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, payment_intent_update, key_store, @@ -254,7 +256,8 @@ impl UpdateTracker, api::PaymentsCancelRequest> for .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; } - db.store + state + .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), storage::PaymentAttemptUpdate::VoidUpdate { diff --git a/crates/router/src/core/payments/operations/payment_capture.rs b/crates/router/src/core/payments/operations/payment_capture.rs index f6e6dee14a53..03a3d031e1a3 100644 --- a/crates/router/src/core/payments/operations/payment_capture.rs +++ b/crates/router/src/core/payments/operations/payment_capture.rs @@ -54,6 +54,7 @@ impl GetTracker, api::PaymentsCaptu payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -135,7 +136,7 @@ impl GetTracker, api::PaymentsCaptu amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -145,7 +146,7 @@ impl GetTracker, api::PaymentsCaptu .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -155,7 +156,7 @@ impl GetTracker, api::PaymentsCaptu .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, diff --git a/crates/router/src/core/payments/operations/payment_complete_authorize.rs b/crates/router/src/core/payments/operations/payment_complete_authorize.rs index 9c48df1dd53f..c6875392750d 100644 --- a/crates/router/src/core/payments/operations/payment_complete_authorize.rs +++ b/crates/router/src/core/payments/operations/payment_complete_authorize.rs @@ -17,7 +17,6 @@ use crate::{ }, utils as core_utils, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -56,6 +55,7 @@ impl GetTracker, api::PaymentsRequest> for Co payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -200,7 +200,7 @@ impl GetTracker, api::PaymentsRequest> for Co )?; let shipping_address = helpers::create_or_update_address_for_payment_by_request( - db, + state, request.shipping.as_ref(), payment_intent.shipping_address_id.clone().as_deref(), merchant_id.as_ref(), @@ -216,7 +216,7 @@ impl GetTracker, api::PaymentsRequest> for Co .map(|shipping_address| shipping_address.address_id.clone()); let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -226,7 +226,7 @@ impl GetTracker, api::PaymentsRequest> for Co .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -364,7 +364,7 @@ impl Domain for CompleteAuthorize { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -377,8 +377,8 @@ impl Domain for CompleteAuthorize { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -478,6 +478,7 @@ impl UpdateTracker, api::PaymentsRequest> for Comple let updated_payment_intent = db .update_payment_intent( + &state.into(), payment_intent, payment_intent_update, key_store, diff --git a/crates/router/src/core/payments/operations/payment_confirm.rs b/crates/router/src/core/payments/operations/payment_confirm.rs index 2955b0c38500..addd2f306702 100644 --- a/crates/router/src/core/payments/operations/payment_confirm.rs +++ b/crates/router/src/core/payments/operations/payment_confirm.rs @@ -7,7 +7,10 @@ use api_models::{ payments::{AdditionalPaymentData, ExtendedCardInfo}, }; use async_trait::async_trait; -use common_utils::ext_traits::{AsyncExt, Encode, StringExt, ValueExt}; +use common_utils::{ + ext_traits::{AsyncExt, Encode, StringExt, ValueExt}, + types::keymanager::Identifier, +}; use error_stack::{report, ResultExt}; use futures::FutureExt; use hyperswitch_domain_models::payments::payment_intent::PaymentIntentUpdateFields; @@ -30,7 +33,6 @@ use crate::{ }, utils as core_utils, }, - db::StorageInterface, events::audit_events::{AuditEvent, AuditEventType}, routes::{app::ReqState, SessionState}, services, @@ -74,6 +76,7 @@ impl GetTracker, api::PaymentsRequest> for Pa // Parallel calls - level 0 let mut payment_intent = store .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, m_merchant_id.as_str(), key_store, @@ -181,13 +184,13 @@ impl GetTracker, api::PaymentsRequest> for Pa let m_payment_intent_payment_id = payment_intent.payment_id.clone(); let m_customer_details_customer_id = customer_details.customer_id.clone(); let m_payment_intent_customer_id = payment_intent.customer_id.clone(); - let store = state.clone().store; let m_key_store = key_store.clone(); + let session_state = state.clone(); let shipping_address_fut = tokio::spawn( async move { helpers::create_or_update_address_for_payment_by_request( - store.as_ref(), + &session_state, m_request_shipping.as_ref(), m_payment_intent_shipping_address_id.as_deref(), m_merchant_id.as_str(), @@ -209,13 +212,13 @@ impl GetTracker, api::PaymentsRequest> for Pa let m_payment_intent_customer_id = payment_intent.customer_id.clone(); let m_payment_intent_billing_address_id = payment_intent.billing_address_id.clone(); let m_payment_intent_payment_id = payment_intent.payment_id.clone(); - let store = state.clone().store; let m_key_store = key_store.clone(); + let session_state = state.clone(); let billing_address_fut = tokio::spawn( async move { helpers::create_or_update_address_for_payment_by_request( - store.as_ref(), + &session_state, m_request_billing.as_ref(), m_payment_intent_billing_address_id.as_deref(), m_merchant_id.as_ref(), @@ -306,7 +309,7 @@ impl GetTracker, api::PaymentsRequest> for Pa request, payment_intent, payment_attempt, - &*state.store, + state, key_store, storage_scheme, ) @@ -467,8 +470,6 @@ impl GetTracker, api::PaymentsRequest> for Pa .in_current_span(), ); - let store = state.clone().store; - let n_payment_method_billing_address_id = payment_attempt.payment_method_billing_address_id.clone(); let n_request_payment_method_billing_address = request @@ -480,11 +481,12 @@ impl GetTracker, api::PaymentsRequest> for Pa let m_key_store = key_store.clone(); let m_customer_details_customer_id = customer_details.customer_id.clone(); let m_merchant_id = merchant_id.clone(); + let session_state = state.clone(); let payment_method_billing_future = tokio::spawn( async move { helpers::create_or_update_address_for_payment_by_request( - store.as_ref(), + &session_state, n_request_payment_method_billing_address.as_ref(), n_payment_method_billing_address_id.as_deref(), m_merchant_id.as_str(), @@ -688,7 +690,7 @@ impl Domain for PaymentConfirm { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -701,8 +703,8 @@ impl Domain for PaymentConfirm { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -1077,12 +1079,15 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to encode additional pm data")?; + let key_manager_state = &state.into(); let encode_additional_pm_to_value = if let Some(ref pm) = payment_data.payment_method_info { let key = key_store.key.get_inner().peek(); let card_detail_from_locker: Option = decrypt::( + key_manager_state, pm.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key, ) .await @@ -1243,7 +1248,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let billing_address = payment_data.address.get_payment_billing(); let billing_details = billing_address - .async_map(|billing_details| create_encrypted_data(key_store, billing_details)) + .async_map(|billing_details| create_encrypted_data(state, key_store, billing_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1251,7 +1256,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let shipping_address = payment_data.address.get_shipping(); let shipping_details = shipping_address - .async_map(|shipping_details| create_encrypted_data(key_store, shipping_details)) + .async_map(|shipping_details| create_encrypted_data(state, key_store, shipping_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1273,10 +1278,12 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let m_storage_scheme = storage_scheme.to_string(); let session_expiry = m_payment_data_payment_intent.session_expiry; let m_key_store = key_store.clone(); + let key_manager_state = state.into(); let payment_intent_fut = tokio::spawn( async move { m_db.update_payment_intent( + &key_manager_state, m_payment_data_payment_intent, storage::PaymentIntentUpdate::Update(Box::new(PaymentIntentUpdateFields { amount: payment_data.payment_intent.amount, @@ -1320,10 +1327,13 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let m_customer_merchant_id = customer.merchant_id.to_owned(); let m_key_store = key_store.clone(); let m_updated_customer = updated_customer.clone(); - let m_db = state.clone().store; + let session_state = state.clone(); + let m_db = session_state.store.clone(); + let key_manager_state = state.into(); tokio::spawn( async move { m_db.update_customer_by_customer_id_merchant_id( + &key_manager_state, m_customer_customer_id, m_customer_merchant_id, customer, diff --git a/crates/router/src/core/payments/operations/payment_create.rs b/crates/router/src/core/payments/operations/payment_create.rs index 18dc2c5f39a6..929f404eae92 100644 --- a/crates/router/src/core/payments/operations/payment_create.rs +++ b/crates/router/src/core/payments/operations/payment_create.rs @@ -6,7 +6,7 @@ use api_models::{ use async_trait::async_trait; use common_utils::{ ext_traits::{AsyncExt, Encode, ValueExt}, - types::MinorUnit, + types::{keymanager::Identifier, MinorUnit}, }; use diesel_models::{ephemeral_key, PaymentMethod}; use error_stack::{self, ResultExt}; @@ -32,7 +32,10 @@ use crate::{ utils as core_utils, }, db::StorageInterface, - routes::{app::ReqState, SessionState}, + routes::{ + app::{ReqState, SessionStateInfo}, + SessionState, + }, services, types::{ api::{self, PaymentIdTypeExt}, @@ -143,7 +146,7 @@ impl GetTracker, api::PaymentsRequest> for Pa let customer_details = helpers::get_customer_details_from_request(request); let shipping_address = helpers::create_or_find_address_for_payment_by_request( - db, + state, request.shipping.as_ref(), None, merchant_id, @@ -155,7 +158,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .await?; let billing_address = helpers::create_or_find_address_for_payment_by_request( - db, + state, request.billing.as_ref(), None, merchant_id, @@ -168,7 +171,7 @@ impl GetTracker, api::PaymentsRequest> for Pa let payment_method_billing_address = helpers::create_or_find_address_for_payment_by_request( - db, + state, request .payment_method_data .as_ref() @@ -286,7 +289,12 @@ impl GetTracker, api::PaymentsRequest> for Pa .await?; payment_intent = db - .insert_payment_intent(payment_intent_new, merchant_key_store, storage_scheme) + .insert_payment_intent( + &state.into(), + payment_intent_new, + merchant_key_store, + storage_scheme, + ) .await .to_duplicate_response(errors::ApiErrorResponse::DuplicatePayment { payment_id: payment_id.clone(), @@ -475,7 +483,7 @@ impl Domain for PaymentCreate { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -488,8 +496,8 @@ impl Domain for PaymentCreate { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -638,12 +646,15 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let raw_customer_details = customer .map(|customer| CustomerData::try_from(customer.clone())) .transpose()?; + let session_state = state.session_state(); // Updation of Customer Details for the cases where both customer_id and specific customer // details are provided in Payment Create Request let customer_details = raw_customer_details .clone() - .async_map(|customer_details| create_encrypted_data(key_store, customer_details)) + .async_map(|customer_details| { + create_encrypted_data(&session_state, key_store, customer_details) + }) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -652,6 +663,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, storage::PaymentIntentUpdate::PaymentCreateUpdate { return_url: None, @@ -836,7 +848,9 @@ impl PaymentCreate { .as_ref() .async_map(|pm_info| async { decrypt::( + &state.into(), pm_info.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -983,7 +997,7 @@ impl PaymentCreate { #[instrument(skip_all)] #[allow(clippy::too_many_arguments)] async fn make_payment_intent( - _state: &SessionState, + state: &SessionState, payment_id: &str, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, @@ -1057,7 +1071,7 @@ impl PaymentCreate { let billing_details = request .billing .clone() - .async_map(|billing_details| create_encrypted_data(key_store, billing_details)) + .async_map(|billing_details| create_encrypted_data(state, key_store, billing_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1068,7 +1082,7 @@ impl PaymentCreate { let shipping_details = request .shipping .clone() - .async_map(|shipping_details| create_encrypted_data(key_store, shipping_details)) + .async_map(|shipping_details| create_encrypted_data(state, key_store, shipping_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1093,7 +1107,7 @@ impl PaymentCreate { // Encrypting our Customer Details to be stored in Payment Intent let customer_details = raw_customer_details - .async_map(|customer_details| create_encrypted_data(key_store, customer_details)) + .async_map(|customer_details| create_encrypted_data(state, key_store, customer_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/core/payments/operations/payment_reject.rs b/crates/router/src/core/payments/operations/payment_reject.rs index a3a8130a9f67..a83079a6a434 100644 --- a/crates/router/src/core/payments/operations/payment_reject.rs +++ b/crates/router/src/core/payments/operations/payment_reject.rs @@ -48,6 +48,7 @@ impl GetTracker, PaymentsCancelRequest> for P let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -79,7 +80,7 @@ impl GetTracker, PaymentsCancelRequest> for P .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -89,7 +90,7 @@ impl GetTracker, PaymentsCancelRequest> for P .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -99,7 +100,7 @@ impl GetTracker, PaymentsCancelRequest> for P .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -234,6 +235,7 @@ impl UpdateTracker, PaymentsCancelRequest> for Payme payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, intent_status_update, key_store, diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs index fc075f29799b..808b2ae6181e 100644 --- a/crates/router/src/core/payments/operations/payment_response.rs +++ b/crates/router/src/core/payments/operations/payment_response.rs @@ -2,7 +2,10 @@ use std::collections::HashMap; use async_trait::async_trait; use common_enums::AuthorizationStatus; -use common_utils::{ext_traits::Encode, types::MinorUnit}; +use common_utils::{ + ext_traits::Encode, + types::{keymanager::KeyManagerState, MinorUnit}, +}; use error_stack::{report, ResultExt}; use futures::FutureExt; use hyperswitch_domain_models::payments::payment_attempt::PaymentAttempt; @@ -265,7 +268,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu { async fn update_tracker<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, _payment_id: &api::PaymentIdType, mut payment_data: PaymentData, router_data: types::RouterData< @@ -317,7 +320,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu }; //payment_attempt update if let Some(payment_attempt_update) = option_payment_attempt_update { - payment_data.payment_attempt = db + payment_data.payment_attempt = state .store .update_payment_attempt_with_attempt_id( payment_data.payment_attempt.clone(), @@ -329,9 +332,10 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu } // payment_intent update if let Some(payment_intent_update) = option_payment_intent_update { - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), payment_intent_update, key_store, @@ -370,7 +374,8 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu "missing authorization_id in incremental_authorization_details in payment_data", ), )?; - db.store + state + .store .update_authorization_by_merchant_id_authorization_id( router_data.merchant_id.clone(), authorization_id, @@ -380,7 +385,7 @@ impl PostUpdateTracker, types::PaymentsIncrementalAu .to_not_found_response(errors::ApiErrorResponse::InternalServerError) .attach_printable("failed while updating authorization")?; //Fetch all the authorizations of the payment and send in incremental authorization response - let authorizations = db + let authorizations = state .store .find_all_authorizations_by_merchant_id_payment_id( &router_data.merchant_id, @@ -1255,9 +1260,11 @@ async fn payment_response_update_tracker( let m_key_store = key_store.clone(); let m_payment_data_payment_intent = payment_data.payment_intent.clone(); let m_payment_intent_update = payment_intent_update.clone(); + let key_manager_state: KeyManagerState = state.into(); let payment_intent_fut = tokio::spawn( async move { m_db.update_payment_intent( + &key_manager_state, m_payment_data_payment_intent, m_payment_intent_update, &m_key_store, diff --git a/crates/router/src/core/payments/operations/payment_session.rs b/crates/router/src/core/payments/operations/payment_session.rs index 503a5c5f2bba..00f599d40955 100644 --- a/crates/router/src/core/payments/operations/payment_session.rs +++ b/crates/router/src/core/payments/operations/payment_session.rs @@ -13,7 +13,6 @@ use crate::{ errors::{self, RouterResult, StorageErrorExt}, payments::{self, helpers, operations, PaymentData}, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -53,6 +52,7 @@ impl GetTracker, api::PaymentsSessionRequest> let mut payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -89,7 +89,7 @@ impl GetTracker, api::PaymentsSessionRequest> let amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -99,7 +99,7 @@ impl GetTracker, api::PaymentsSessionRequest> .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -109,7 +109,7 @@ impl GetTracker, api::PaymentsSessionRequest> .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -244,6 +244,7 @@ impl UpdateTracker, api::PaymentsSessionRequest> for Some(metadata) => state .store .update_payment_intent( + &state.into(), payment_data.payment_intent, storage::PaymentIntentUpdate::MetadataUpdate { metadata, @@ -295,7 +296,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -308,8 +309,8 @@ where errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -358,6 +359,7 @@ where let all_connector_accounts = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), &merchant_account.merchant_id, false, key_store, diff --git a/crates/router/src/core/payments/operations/payment_start.rs b/crates/router/src/core/payments/operations/payment_start.rs index c4096c6ea5aa..98befe122cc8 100644 --- a/crates/router/src/core/payments/operations/payment_start.rs +++ b/crates/router/src/core/payments/operations/payment_start.rs @@ -12,7 +12,6 @@ use crate::{ errors::{self, CustomResult, RouterResult, StorageErrorExt}, payments::{helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -51,6 +50,7 @@ impl GetTracker, api::PaymentsStartRequest> f payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -86,7 +86,7 @@ impl GetTracker, api::PaymentsStartRequest> f amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id, @@ -96,7 +96,7 @@ impl GetTracker, api::PaymentsStartRequest> f .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -106,7 +106,7 @@ impl GetTracker, api::PaymentsStartRequest> f .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id, @@ -268,7 +268,7 @@ where #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -281,8 +281,8 @@ where errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, diff --git a/crates/router/src/core/payments/operations/payment_status.rs b/crates/router/src/core/payments/operations/payment_status.rs index 5f20ccdf2779..fff096d9b4c5 100644 --- a/crates/router/src/core/payments/operations/payment_status.rs +++ b/crates/router/src/core/payments/operations/payment_status.rs @@ -2,7 +2,7 @@ use std::marker::PhantomData; use api_models::enums::FrmSuggestion; use async_trait::async_trait; -use common_utils::ext_traits::AsyncExt; +use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState}; use error_stack::ResultExt; use router_derive::PaymentOperation; use router_env::{instrument, logger, tracing}; @@ -16,7 +16,6 @@ use crate::{ PaymentData, }, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -58,7 +57,7 @@ impl Domain for PaymentStatus { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -71,8 +70,8 @@ impl Domain for PaymentStatus { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -197,7 +196,7 @@ impl GetTracker, api::PaymentsRetrieveRequest payment_id, merchant_account, key_store, - &*state.store, + state, request, self, merchant_account.storage_scheme, @@ -214,7 +213,7 @@ async fn get_tracker_for_sync< payment_id: &api::PaymentIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, - db: &dyn StorageInterface, + state: &SessionState, request: &api::PaymentsRetrieveRequest, operation: Op, storage_scheme: enums::MerchantStorageScheme, @@ -222,7 +221,7 @@ async fn get_tracker_for_sync< let (payment_intent, mut payment_attempt, currency, amount); (payment_intent, payment_attempt) = get_payment_intent_payment_attempt( - db, + state, payment_id, &merchant_account.merchant_id, key_store, @@ -238,7 +237,7 @@ async fn get_tracker_for_sync< amount = payment_attempt.get_total_amount().into(); let shipping_address = helpers::get_address_by_id( - db, + state, payment_intent.shipping_address_id.clone(), key_store, &payment_intent.payment_id.clone(), @@ -247,7 +246,7 @@ async fn get_tracker_for_sync< ) .await?; let billing_address = helpers::get_address_by_id( - db, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id.clone(), @@ -257,7 +256,7 @@ async fn get_tracker_for_sync< .await?; let payment_method_billing = helpers::get_address_by_id( - db, + state, payment_attempt.payment_method_billing_address_id.clone(), key_store, &payment_intent.payment_id.clone(), @@ -267,7 +266,7 @@ async fn get_tracker_for_sync< .await?; payment_attempt.encoded_data.clone_from(&request.param); - + let db = &*state.store; let attempts = match request.expand_attempts { Some(true) => { Some(db @@ -510,18 +509,21 @@ impl ValidateRequest for Payme } pub async fn get_payment_intent_payment_attempt( - db: &dyn StorageInterface, + state: &SessionState, payment_id: &api::PaymentIdType, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: enums::MerchantStorageScheme, ) -> RouterResult<(storage::PaymentIntent, storage::PaymentAttempt)> { + let key_manager_state: KeyManagerState = state.into(); + let db = &*state.store; let get_pi_pa = || async { let (pi, pa); match payment_id { api_models::payments::PaymentIdType::PaymentIntentId(ref id) => { pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, id, merchant_id, key_store, @@ -547,6 +549,7 @@ pub async fn get_payment_intent_payment_attempt( .await?; pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, pa.payment_id.as_str(), merchant_id, key_store, @@ -560,6 +563,7 @@ pub async fn get_payment_intent_payment_attempt( .await?; pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, pa.payment_id.as_str(), merchant_id, key_store, @@ -578,6 +582,7 @@ pub async fn get_payment_intent_payment_attempt( pi = db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, pa.payment_id.as_str(), merchant_id, key_store, diff --git a/crates/router/src/core/payments/operations/payment_update.rs b/crates/router/src/core/payments/operations/payment_update.rs index 98006e574ca2..f95ec0d01433 100644 --- a/crates/router/src/core/payments/operations/payment_update.rs +++ b/crates/router/src/core/payments/operations/payment_update.rs @@ -24,7 +24,6 @@ use crate::{ payments::{self, helpers, operations, CustomerDetails, PaymentAddress, PaymentData}, utils as core_utils, }, - db::StorageInterface, routes::{app::ReqState, SessionState}, services, types::{ @@ -64,6 +63,7 @@ impl GetTracker, api::PaymentsRequest> for Pa payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -193,7 +193,7 @@ impl GetTracker, api::PaymentsRequest> for Pa } let shipping_address = helpers::create_or_update_address_for_payment_by_request( - db, + state, request.shipping.as_ref(), payment_intent.shipping_address_id.as_deref(), merchant_id, @@ -207,7 +207,7 @@ impl GetTracker, api::PaymentsRequest> for Pa ) .await?; let billing_address = helpers::create_or_update_address_for_payment_by_request( - db, + state, request.billing.as_ref(), payment_intent.billing_address_id.as_deref(), merchant_id, @@ -222,7 +222,7 @@ impl GetTracker, api::PaymentsRequest> for Pa .await?; let payment_method_billing = helpers::create_or_update_address_for_payment_by_request( - db, + state, request .payment_method_data .as_ref() @@ -491,7 +491,7 @@ impl Domain for PaymentUpdate { #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - db: &dyn StorageInterface, + state: &SessionState, payment_data: &mut PaymentData, request: Option, key_store: &domain::MerchantKeyStore, @@ -504,8 +504,8 @@ impl Domain for PaymentUpdate { errors::StorageError, > { helpers::create_customer_if_not_exist( + state, Box::new(self), - db, payment_data, request, &key_store.merchant_id, @@ -715,7 +715,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let billing_details = payment_data .address .get_payment_billing() - .async_map(|billing_details| create_encrypted_data(key_store, billing_details)) + .async_map(|billing_details| create_encrypted_data(state, key_store, billing_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -724,7 +724,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen let shipping_details = payment_data .address .get_shipping() - .async_map(|shipping_details| create_encrypted_data(key_store, shipping_details)) + .async_map(|shipping_details| create_encrypted_data(state, key_store, shipping_details)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -741,6 +741,7 @@ impl UpdateTracker, api::PaymentsRequest> for Paymen payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::Update(Box::new(PaymentIntentUpdateFields { amount: payment_data.amount.into(), diff --git a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs index 61a629df6226..20c2e051b61b 100644 --- a/crates/router/src/core/payments/operations/payments_incremental_authorization.rs +++ b/crates/router/src/core/payments/operations/payments_incremental_authorization.rs @@ -16,10 +16,7 @@ use crate::{ PaymentAddress, }, }, - routes::{ - app::{ReqState, StorageInterface}, - SessionState, - }, + routes::{app::ReqState, SessionState}, services, types::{ api::{self, PaymentIdTypeExt}, @@ -59,6 +56,7 @@ impl let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &state.into(), &payment_id, merchant_id, key_store, @@ -179,7 +177,7 @@ impl UpdateTracker, PaymentsIncrementalAut #[instrument(skip_all)] async fn update_trackers<'b>( &'b self, - db: &'b SessionState, + state: &'b SessionState, _req_state: ReqState, mut payment_data: payments::PaymentData, _customer: Option, @@ -224,7 +222,7 @@ impl UpdateTracker, PaymentsIncrementalAut connector_authorization_id: None, previously_authorized_amount: payment_data.payment_intent.amount, }; - let authorization = db + let authorization = state .store .insert_authorization(authorization_new.clone()) .await @@ -236,9 +234,10 @@ impl UpdateTracker, PaymentsIncrementalAut }) .attach_printable("failed while inserting new authorization")?; // Update authorization_count in payment_intent - payment_data.payment_intent = db + payment_data.payment_intent = state .store .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::AuthorizationCountUpdate { authorization_count: new_authorization_count, @@ -295,7 +294,7 @@ impl Domain #[instrument(skip_all)] async fn get_or_create_customer_details<'a>( &'a self, - _db: &dyn StorageInterface, + _state: &SessionState, _payment_data: &mut payments::PaymentData, _request: Option, _merchant_key_store: &domain::MerchantKeyStore, diff --git a/crates/router/src/core/payments/retry.rs b/crates/router/src/core/payments/retry.rs index bf5ae5aa6350..99cb6aaec689 100644 --- a/crates/router/src/core/payments/retry.rs +++ b/crates/router/src/core/payments/retry.rs @@ -469,6 +469,7 @@ where payment_data.payment_intent = db .update_payment_intent( + &state.into(), payment_data.payment_intent.clone(), storage::PaymentIntentUpdate::PaymentAttemptAndAttemptCountUpdate { active_attempt_id: payment_data.payment_attempt.attempt_id.clone(), diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs index 0ff53c63b484..ff9f6ddbf3aa 100644 --- a/crates/router/src/core/payments/routing.rs +++ b/crates/router/src/core/payments/routing.rs @@ -528,6 +528,7 @@ pub async fn refresh_cgraph_cache<'a>( let mut merchant_connector_accounts = state .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), &key_store.merchant_id, false, key_store, diff --git a/crates/router/src/core/payments/tokenization.rs b/crates/router/src/core/payments/tokenization.rs index b77bdb7515e9..40163367139c 100644 --- a/crates/router/src/core/payments/tokenization.rs +++ b/crates/router/src/core/payments/tokenization.rs @@ -8,7 +8,7 @@ use common_utils::{ id_type, pii, }; use error_stack::{report, ResultExt}; -use masking::{ExposeInterface, PeekInterface, Secret}; +use masking::{ExposeInterface, Secret}; use router_env::{instrument, metrics::add_attributes, tracing}; use super::helpers; @@ -214,7 +214,7 @@ where let pm_data_encrypted: Option>> = pm_card_details - .async_map(|pm_card| create_encrypted_data(key_store, pm_card)) + .async_map(|pm_card| create_encrypted_data(state, key_store, pm_card)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -223,7 +223,7 @@ where let encrypted_payment_method_billing_address: Option< Encryptable>, > = payment_method_billing_address - .async_map(|address| create_encrypted_data(key_store, address.clone())) + .async_map(|address| create_encrypted_data(state, key_store, address.clone())) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -310,7 +310,7 @@ where let pm_metadata = create_payment_method_metadata(None, connector_token)?; payment_methods::cards::create_payment_method( - db, + state, &payment_method_create_request, &customer_id, &resp.payment_method_id, @@ -407,7 +407,7 @@ where Err(err) => { if err.current_context().is_db_not_found() { payment_methods::cards::insert_payment_method( - db, + state, &resp, &payment_method_create_request.clone(), key_store, @@ -479,10 +479,8 @@ where ))? }; - let existing_pm_data = payment_methods::cards::get_card_details_without_locker_fallback( - &existing_pm, - key_store.key.peek(), - state, + let existing_pm_data = payment_methods::cards::get_card_details_without_locker_fallback(&existing_pm,state, + key_store, ) .await?; @@ -518,7 +516,7 @@ where let pm_data_encrypted: Option< Encryptable>, > = updated_pmd - .async_map(|pmd| create_encrypted_data(key_store, pmd)) + .async_map(|pmd| create_encrypted_data(state, key_store, pmd)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) @@ -601,7 +599,7 @@ where resp.payment_method_id = generate_id(consts::ID_LENGTH, "pm"); payment_methods::cards::create_payment_method( - db, + state, &payment_method_create_request, &customer_id, &resp.payment_method_id, diff --git a/crates/router/src/core/payout_link.rs b/crates/router/src/core/payout_link.rs index 3de8e505499b..78ff9900f666 100644 --- a/crates/router/src/core/payout_link.rs +++ b/crates/router/src/core/payout_link.rs @@ -122,6 +122,7 @@ pub async fn initiate_payout_link( // Fetch customer let customer = db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &customer_id, &req.merchant_id, &key_store, @@ -263,10 +264,12 @@ pub async fn filter_payout_methods( key_store: &domain::MerchantKeyStore, payout: &hyperswitch_domain_models::payouts::payouts::Payouts, ) -> errors::RouterResult> { - let db: &dyn StorageInterface = &*state.store; + let db = &*state.store; + let key_manager_state = &state.into(); //Fetch all merchant connector accounts let all_mcas = db .find_merchant_connector_account_by_merchant_id_and_disabled_list( + key_manager_state, &merchant_account.merchant_id, false, key_store, @@ -282,7 +285,7 @@ pub async fn filter_payout_methods( common_enums::ConnectorType::PayoutProcessor, ); let address = db - .find_address_by_address_id(&payout.address_id.clone(), key_store) + .find_address_by_address_id(key_manager_state, &payout.address_id.clone(), key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs index b2c883c381b2..6db3c1edf43c 100644 --- a/crates/router/src/core/payouts.rs +++ b/crates/router/src/core/payouts.rs @@ -732,6 +732,7 @@ pub async fn payouts_list_core( Ok(payout_attempt) => { match db .find_customer_by_customer_id_merchant_id( + &(&state).into(), &payouts.customer_id, merchant_id, &key_store, @@ -822,7 +823,14 @@ pub async fn payouts_filtered_list_core( .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?; let data: Vec = join_all(list.into_iter().map(|(p, pa, c)| async { - match domain::Customer::convert_back(c, &key_store.key).await { + match domain::Customer::convert_back( + &(&state).into(), + c, + &key_store.key, + key_store.merchant_id.clone(), + ) + .await + { Ok(domain_cust) => Some((p, pa, domain_cust)), Err(err) => { logger::warn!( @@ -1084,6 +1092,7 @@ pub async fn create_recipient( { payout_data.customer_details = Some( db.update_customer_by_customer_id_merchant_id( + &state.into(), customer_id, merchant_id, customer, @@ -2184,7 +2193,7 @@ pub async fn payout_create_db_entries( // Get or create address let billing_address = payment_helpers::create_or_find_address_for_payment_by_request( - db, + state, req.billing.as_ref(), None, merchant_id, @@ -2345,7 +2354,7 @@ pub async fn make_payout_data( .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?; let billing_address = payment_helpers::create_or_find_address_for_payment_by_request( - db, + state, None, Some(&payouts.address_id.to_owned()), merchant_id, @@ -2358,6 +2367,7 @@ pub async fn make_payout_data( let customer_details = db .find_customer_optional_by_customer_id_merchant_id( + &state.into(), &payouts.customer_id.to_owned(), merchant_id, key_store, diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs index 3ab171432022..6cda7b284334 100644 --- a/crates/router/src/core/payouts/helpers.rs +++ b/crates/router/src/core/payouts/helpers.rs @@ -1,13 +1,17 @@ -use api_models::{enums, payment_methods::Card, payouts}; +use api_models::{customers::CustomerRequestWithEmail, enums, payment_methods::Card, payouts}; use common_utils::{ + encryption::Encryption, errors::CustomResult, ext_traits::{AsyncExt, StringExt}, fp_utils, generate_customer_id_of_default_length, id_type, - types::MinorUnit, + types::{ + keymanager::{Identifier, KeyManagerState, ToEncryptable}, + MinorUnit, + }, }; -use diesel_models::encryption::Encryption; use error_stack::{report, ResultExt}; -use masking::{ExposeInterface, PeekInterface, Secret}; +use hyperswitch_domain_models::type_encryption::batch_encrypt; +use masking::{PeekInterface, Secret}; use router_env::logger; use super::PayoutData; @@ -233,6 +237,7 @@ pub async fn save_payout_data_to_locker( } _ => { let key = key_store.key.get_inner().peek(); + let key_manager_state: KeyManagerState = state.into(); let enc_data = async { serde_json::to_value(payout_method_data.to_owned()) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -242,7 +247,14 @@ pub async fn save_payout_data_to_locker( let secret: Secret = Secret::new(v.to_string()); secret }) - .async_lift(|inner| domain_types::encrypt_optional(inner, key)) + .async_lift(|inner| { + domain_types::encrypt_optional( + &key_manager_state, + inner, + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + }) .await } .await @@ -453,7 +465,7 @@ pub async fn save_payout_data_to_locker( }); ( Some( - cards::create_encrypted_data(key_store, pm_data) + cards::create_encrypted_data(state, key_store, pm_data) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt customer details")?, @@ -489,7 +501,7 @@ pub async fn save_payout_data_to_locker( if should_insert_in_pm_table { let payment_method_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm"); cards::create_payment_method( - db, + state, &new_payment_method, &payout_attempt.customer_id, &payment_method_id, @@ -601,9 +613,11 @@ pub async fn get_or_create_customer_details( let merchant_id = &merchant_account.merchant_id; let key = key_store.key.get_inner().peek(); + let key_manager_state = &state.into(); match db .find_customer_optional_by_customer_id_merchant_id( + key_manager_state, &customer_id, merchant_id, key_store, @@ -614,21 +628,28 @@ pub async fn get_or_create_customer_details( { Some(customer) => Ok(Some(customer)), None => { + let encrypted_data = batch_encrypt( + &state.into(), + CustomerRequestWithEmail::to_encryptable(CustomerRequestWithEmail { + name: customer_details.name.clone(), + email: customer_details.email.clone(), + phone: customer_details.phone.clone(), + }), + Identifier::Merchant(key_store.merchant_id.clone()), + key, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?; + let encryptable_customer = + CustomerRequestWithEmail::from_encryptable(encrypted_data) + .change_context(errors::ApiErrorResponse::InternalServerError)?; + let customer = domain::Customer { customer_id, merchant_id: merchant_id.to_string(), - name: domain_types::encrypt_optional(customer_details.name.to_owned(), key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, - email: domain_types::encrypt_optional( - customer_details.email.to_owned().map(|e| e.expose()), - key, - ) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, - phone: domain_types::encrypt_optional(customer_details.phone.to_owned(), key) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, description: None, phone_country_code: customer_details.phone_country_code.to_owned(), metadata: None, @@ -642,9 +663,14 @@ pub async fn get_or_create_customer_details( }; Ok(Some( - db.insert_customer(customer, key_store, merchant_account.storage_scheme) - .await - .change_context(errors::ApiErrorResponse::InternalServerError)?, + db.insert_customer( + key_manager_state, + customer, + key_store, + merchant_account.storage_scheme, + ) + .await + .change_context(errors::ApiErrorResponse::InternalServerError)?, )) } } diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 3434ac3e9c78..32e5ef308cf8 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -15,7 +15,7 @@ use common_utils::{ crypto::{HmacSha256, SignMessage}, ext_traits::AsyncExt, generate_id, - types::{self as util_types, AmountConvertor}, + types::{self as util_types, keymanager::Identifier, AmountConvertor}, }; use error_stack::ResultExt; use helpers::PaymentAuthConnectorDataExt; @@ -110,7 +110,7 @@ pub async fn create_link_token( > = connector.connector.get_connector_integration(); let payment_intent = oss_helpers::verify_payment_intent_time_and_client_secret( - &*state.store, + &state, &merchant_account, &key_store, payload.client_secret, @@ -121,7 +121,7 @@ pub async fn create_link_token( .as_ref() .async_map(|pi| async { oss_helpers::get_address_by_id( - &*state.store, + &state, pi.billing_address_id.clone(), &key_store, &pi.payment_id, @@ -139,6 +139,7 @@ pub async fn create_link_token( let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &(&state).into(), merchant_account.merchant_id.as_str(), &selected_config.mca_id, &key_store, @@ -234,6 +235,7 @@ pub async fn exchange_token_core( let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &(&state).into(), merchant_account.merchant_id.as_str(), &config.mca_id, &key_store, @@ -294,6 +296,7 @@ async fn store_bank_details_in_payment_methods( let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &payload.payment_id, &merchant_account.merchant_id, &key_store, @@ -326,7 +329,9 @@ async fn store_bank_details_in_payment_methods( for pm in payment_methods { if pm.payment_method == Some(enums::PaymentMethod::BankDebit) { let bank_details_pm_data = decrypt::( + &(&state).into(), pm.payment_method_data.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key, ) .await @@ -433,10 +438,11 @@ async fn store_bank_details_in_payment_methods( ); let payment_method_data = payment_methods::PaymentMethodsData::BankDetails(pmd); - let encrypted_data = cards::create_encrypted_data(&key_store, payment_method_data) - .await - .change_context(ApiErrorResponse::InternalServerError) - .attach_printable("Unable to encrypt customer details")?; + let encrypted_data = + cards::create_encrypted_data(&state, &key_store, payment_method_data) + .await + .change_context(ApiErrorResponse::InternalServerError) + .attach_printable("Unable to encrypt customer details")?; let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate { payment_method_data: Some(encrypted_data.into()), @@ -446,7 +452,7 @@ async fn store_bank_details_in_payment_methods( } else { let payment_method_data = payment_methods::PaymentMethodsData::BankDetails(pmd); let encrypted_data = - cards::create_encrypted_data(&key_store, Some(payment_method_data)) + cards::create_encrypted_data(&state, &key_store, Some(payment_method_data)) .await .change_context(ApiErrorResponse::InternalServerError) .attach_printable("Unable to encrypt customer details")?; @@ -701,14 +707,19 @@ pub async fn retrieve_payment_method_from_auth_service( let connector = PaymentAuthConnectorData::get_connector_by_name( auth_token.connector_details.connector.as_str(), )?; - + let key_manager_state = &state.into(); let merchant_account = db - .find_merchant_account_by_merchant_id(&payment_intent.merchant_id, key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payment_intent.merchant_id, + key_store, + ) .await .to_not_found_response(ApiErrorResponse::MerchantAccountNotFound)?; let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &payment_intent.merchant_id, &auth_token.connector_details.mca_id, key_store, @@ -770,7 +781,7 @@ pub async fn retrieve_payment_method_from_auth_service( } let address = oss_helpers::get_address_by_id( - &*state.store, + state, payment_intent.billing_address_id.clone(), key_store, &payment_intent.payment_id, diff --git a/crates/router/src/core/refunds.rs b/crates/router/src/core/refunds.rs index 5b74eefb9a41..374c37dc2175 100644 --- a/crates/router/src/core/refunds.rs +++ b/crates/router/src/core/refunds.rs @@ -57,6 +57,7 @@ pub async fn refund_create_core( payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), &req.payment_id, merchant_id, &key_store, @@ -415,6 +416,7 @@ pub async fn refund_retrieve_core( let payment_id = refund.payment_id.as_str(); payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(&state).into(), payment_id, merchant_id, &key_store, @@ -924,9 +926,11 @@ pub async fn refund_manual_update( state: SessionState, req: api_models::refunds::RefundManualUpdateRequest, ) -> RouterResponse { + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &req.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -935,7 +939,7 @@ pub async fn refund_manual_update( .attach_printable("Error while fetching the key store by merchant_id")?; let merchant_account = state .store - .find_merchant_account_by_merchant_id(&req.merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &req.merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound) .attach_printable("Error while fetching the merchant_account by merchant_id")?; @@ -1125,6 +1129,7 @@ pub async fn sync_refund_with_gateway_workflow( state: &SessionState, refund_tracker: &storage::ProcessTracker, ) -> Result<(), errors::ProcessTrackerError> { + let key_manager_state = &state.into(); let refund_core = serde_json::from_value::(refund_tracker.tracking_data.clone()) .change_context(errors::ApiErrorResponse::InternalServerError) @@ -1138,6 +1143,7 @@ pub async fn sync_refund_with_gateway_workflow( let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &refund_core.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -1145,7 +1151,11 @@ pub async fn sync_refund_with_gateway_workflow( let merchant_account = state .store - .find_merchant_account_by_merchant_id(&refund_core.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &refund_core.merchant_id, + &key_store, + ) .await?; let response = Box::pin(refund_retrieve_core( @@ -1220,17 +1230,22 @@ pub async fn trigger_refund_execute_workflow( refund_tracker.tracking_data ) })?; - + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &refund_core.merchant_id, &state.store.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(&refund_core.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &refund_core.merchant_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1245,7 +1260,11 @@ pub async fn trigger_refund_execute_workflow( match (&refund.sent_to_gateway, &refund.refund_status) { (false, enums::RefundStatus::Pending) => { let merchant_account = db - .find_merchant_account_by_merchant_id(&refund.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &refund.merchant_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -1261,6 +1280,7 @@ pub async fn trigger_refund_execute_workflow( let payment_intent = db .find_payment_intent_by_payment_id_merchant_id( + &(state.into()), &payment_attempt.payment_id, &refund.merchant_id, &key_store, diff --git a/crates/router/src/core/routing.rs b/crates/router/src/core/routing.rs index 5f10de56e471..b17217737be4 100644 --- a/crates/router/src/core/routing.rs +++ b/crates/router/src/core/routing.rs @@ -120,7 +120,7 @@ pub async fn create_routing_config( .await?; helpers::validate_connectors_in_routing_config( - db, + &state, &key_store, &merchant_account.merchant_id, &profile_id, diff --git a/crates/router/src/core/routing/helpers.rs b/crates/router/src/core/routing/helpers.rs index d99177aa5557..707ed41d95aa 100644 --- a/crates/router/src/core/routing/helpers.rs +++ b/crates/router/src/core/routing/helpers.rs @@ -15,6 +15,7 @@ use storage_impl::redis::cache; use crate::{ core::errors::{self, RouterResult}, db::StorageInterface, + routes::SessionState, types::{domain, storage}, utils::StringExt, }; @@ -186,7 +187,7 @@ pub async fn update_routing_algorithm( /// This will help make one of all configured algorithms to be in active state for a particular /// merchant pub async fn update_merchant_active_algorithm_ref( - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, config_key: cache::CacheKind<'_>, algorithm_id: routing_types::RoutingAlgorithmRef, @@ -218,8 +219,9 @@ pub async fn update_merchant_active_algorithm_ref( payment_link_config: None, pm_collect_link_config: None, }; - + let db = &*state.store; db.update_specific_fields_in_merchant( + &state.into(), &key_store.merchant_id, merchant_account_update, key_store, @@ -300,14 +302,16 @@ pub async fn update_business_profile_active_algorithm_ref( } pub async fn validate_connectors_in_routing_config( - db: &dyn StorageInterface, + state: &SessionState, key_store: &domain::MerchantKeyStore, merchant_id: &str, profile_id: &str, routing_algorithm: &routing_types::RoutingAlgorithm, ) -> RouterResult<()> { - let all_mcas = db + let all_mcas = &*state + .store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + &state.into(), merchant_id, true, key_store, diff --git a/crates/router/src/core/surcharge_decision_config.rs b/crates/router/src/core/surcharge_decision_config.rs index b35d7c5ad285..55ea4ba16f89 100644 --- a/crates/router/src/core/surcharge_decision_config.rs +++ b/crates/router/src/core/surcharge_decision_config.rs @@ -91,7 +91,7 @@ pub async fn upsert_surcharge_decision_config( algo_id.update_surcharge_config_id(key.clone()); let config_key = cache::CacheKind::Surcharge(key.into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -128,7 +128,7 @@ pub async fn upsert_surcharge_decision_config( algo_id.update_surcharge_config_id(key.clone()); let config_key = cache::CacheKind::Surcharge(key.clone().into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update routing algorithm ref")?; @@ -158,7 +158,7 @@ pub async fn delete_surcharge_decision_config( .unwrap_or_default(); algo_id.surcharge_config_algo_id = None; let config_key = cache::CacheKind::Surcharge(key.clone().into()); - update_merchant_active_algorithm_ref(db, &key_store, config_key, algo_id) + update_merchant_active_algorithm_ref(&state, &key_store, config_key, algo_id) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to update deleted algorithm ref")?; diff --git a/crates/router/src/core/user.rs b/crates/router/src/core/user.rs index 461ac855364d..be33ccc69e1f 100644 --- a/crates/router/src/core/user.rs +++ b/crates/router/src/core/user.rs @@ -4,6 +4,7 @@ use api_models::{ payments::RedirectionResponse, user::{self as user_api, InviteMultipleUserResponse}, }; +use common_utils::types::keymanager::Identifier; #[cfg(feature = "email")] use diesel_models::user_role::UserRoleUpdate; use diesel_models::{ @@ -1104,9 +1105,11 @@ pub async fn create_internal_user( state: SessionState, request: user_api::CreateInternalUserRequest, ) -> UserResponse<()> { + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, consts::user_role::INTERNAL_USER_MERCHANT_ID, &state.store.get_master_key().to_vec().into(), ) @@ -1122,6 +1125,7 @@ pub async fn create_internal_user( let internal_merchant = state .store .find_merchant_account_by_merchant_id( + key_manager_state, consts::user_role::INTERNAL_USER_MERCHANT_ID, &key_store, ) @@ -1176,7 +1180,7 @@ pub async fn switch_merchant_id( } let user = user_from_token.get_user_from_db(&state).await?; - + let key_manager_state = &(&state).into(); let role_info = roles::RoleInfo::from_role_id( &state, &user_from_token.role_id, @@ -1190,6 +1194,7 @@ pub async fn switch_merchant_id( let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, request.merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) @@ -1204,7 +1209,11 @@ pub async fn switch_merchant_id( let org_id = state .store - .find_merchant_account_by_merchant_id(request.merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + request.merchant_id.as_str(), + &key_store, + ) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -1306,6 +1315,7 @@ pub async fn list_merchants_for_user( let merchant_accounts = state .store .list_multiple_merchant_accounts( + &(&state).into(), user_roles .iter() .map(|role| role.merchant_id.clone()) @@ -1852,7 +1862,9 @@ pub async fn update_totp( totp_secret: Some( // TODO: Impl conversion trait for User and move this there domain::types::encrypt::( + &(&state).into(), totp.get_secret_base32().into(), + Identifier::User(key_store.user_id.clone()), key_store.key.peek(), ) .await @@ -1918,7 +1930,10 @@ pub async fn transfer_user_key_store_keymanager( let db = &state.global_store; let key_stores = db - .get_all_user_key_store(&state.store.get_master_key().to_vec().into()) + .get_all_user_key_store( + &(&state).into(), + &state.store.get_master_key().to_vec().into(), + ) .await .change_context(UserErrors::InternalServerError)?; @@ -2056,10 +2071,12 @@ pub async fn create_user_authentication_method( ) .change_context(UserErrors::InternalServerError) .attach_printable("Failed to decode DEK")?; - + let id = uuid::Uuid::new_v4().to_string(); let (private_config, public_config) = utils::user::construct_public_and_private_db_configs( + &state, &req.auth_method, &user_auth_encryption_key, + id.clone(), ) .await?; @@ -2103,7 +2120,7 @@ pub async fn create_user_authentication_method( state .store .insert_user_authentication_method(UserAuthenticationMethodNew { - id: uuid::Uuid::new_v4().to_string(), + id, auth_id, owner_id: req.owner_id, owner_type: req.owner_type, @@ -2137,8 +2154,10 @@ pub async fn update_user_authentication_method( .attach_printable("Failed to decode DEK")?; let (private_config, public_config) = utils::user::construct_public_and_private_db_configs( + &state, &req.auth_method, &user_auth_encryption_key, + req.id.clone(), ) .await?; @@ -2212,9 +2231,12 @@ pub async fn get_sso_auth_url( .await .to_not_found_response(UserErrors::InvalidUserAuthMethodOperation)?; - let open_id_private_config = - utils::user::decrypt_oidc_private_config(&state, user_authentication_method.private_config) - .await?; + let open_id_private_config = utils::user::decrypt_oidc_private_config( + &state, + user_authentication_method.private_config, + request.id.clone(), + ) + .await?; let open_id_public_config = serde_json::from_value::( user_authentication_method @@ -2264,9 +2286,12 @@ pub async fn sso_sign( .await .change_context(UserErrors::InternalServerError)?; - let open_id_private_config = - utils::user::decrypt_oidc_private_config(&state, user_authentication_method.private_config) - .await?; + let open_id_private_config = utils::user::decrypt_oidc_private_config( + &state, + user_authentication_method.private_config, + authentication_method_id, + ) + .await?; let open_id_public_config = serde_json::from_value::( user_authentication_method diff --git a/crates/router/src/core/user/dashboard_metadata.rs b/crates/router/src/core/user/dashboard_metadata.rs index 364898462946..b279d852d40d 100644 --- a/crates/router/src/core/user/dashboard_metadata.rs +++ b/crates/router/src/core/user/dashboard_metadata.rs @@ -613,6 +613,7 @@ pub async fn backfill_metadata( let key_store = state .store .get_merchant_key_store_by_merchant_id( + &state.into(), &user.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -713,6 +714,7 @@ pub async fn get_merchant_connector_account_by_name( state .store .find_merchant_connector_account_by_merchant_id_connector_name( + &state.into(), merchant_id, connector_name, key_store, diff --git a/crates/router/src/core/user/sample_data.rs b/crates/router/src/core/user/sample_data.rs index e1ccb3b03355..b679753b56c1 100644 --- a/crates/router/src/core/user/sample_data.rs +++ b/crates/router/src/core/user/sample_data.rs @@ -25,6 +25,7 @@ pub async fn generate_sample_data_for_user( let key_store = state .store .get_merchant_key_store_by_merchant_id( + &(&state).into(), &user_from_token.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -50,7 +51,7 @@ pub async fn generate_sample_data_for_user( state .store - .insert_payment_intents_batch_for_sample_data(payment_intents, &key_store) + .insert_payment_intents_batch_for_sample_data(&(&state).into(), payment_intents, &key_store) .await .switch()?; state @@ -74,10 +75,11 @@ pub async fn delete_sample_data_for_user( _req_state: ReqState, ) -> SampleDataApiResponse<()> { let merchant_id_del = user_from_token.merchant_id.as_str(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &user_from_token.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -87,7 +89,7 @@ pub async fn delete_sample_data_for_user( state .store - .delete_payment_intents_for_sample_data(merchant_id_del, &key_store) + .delete_payment_intents_for_sample_data(key_manager_state, merchant_id_del, &key_store) .await .switch()?; state diff --git a/crates/router/src/core/verification.rs b/crates/router/src/core/verification.rs index b6e71b2828fa..65ac56883252 100644 --- a/crates/router/src/core/verification.rs +++ b/crates/router/src/core/verification.rs @@ -91,13 +91,19 @@ pub async fn get_verified_apple_domains_with_mid_mca_id( errors::ApiErrorResponse, > { let db = state.store.as_ref(); + let key_manager_state = &(&state).into(); let key_store = db - .get_merchant_key_store_by_merchant_id(&merchant_id, &db.get_master_key().to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + &merchant_id, + &db.get_master_key().to_vec().into(), + ) .await .change_context(errors::ApiErrorResponse::MerchantAccountNotFound)?; let verified_domains = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id.as_str(), merchant_connector_id.as_str(), &key_store, diff --git a/crates/router/src/core/verification/utils.rs b/crates/router/src/core/verification/utils.rs index bfbc1cb8b448..a59789b2802d 100644 --- a/crates/router/src/core/verification/utils.rs +++ b/crates/router/src/core/verification/utils.rs @@ -15,9 +15,11 @@ pub async fn check_existence_and_add_domain_to_db( merchant_connector_id: String, domain_from_req: Vec, ) -> CustomResult, errors::ApiErrorResponse> { + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -27,6 +29,7 @@ pub async fn check_existence_and_add_domain_to_db( let merchant_connector_account = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_id, &merchant_connector_id, &key_store, @@ -66,6 +69,7 @@ pub async fn check_existence_and_add_domain_to_db( state .store .update_merchant_connector_account( + key_manager_state, merchant_connector_account, updated_mca.into(), &key_store, diff --git a/crates/router/src/core/webhooks/incoming.rs b/crates/router/src/core/webhooks/incoming.rs index 9eb74dc2df4f..5c5346ce1349 100644 --- a/crates/router/src/core/webhooks/incoming.rs +++ b/crates/router/src/core/webhooks/incoming.rs @@ -258,7 +258,7 @@ async fn incoming_webhooks_core( Some(merchant_connector_account) => merchant_connector_account, None => { helper_utils::get_mca_from_object_reference_id( - &*state.clone().store, + &state, object_ref_id.clone(), &merchant_account, &connector_name, @@ -1675,6 +1675,7 @@ async fn fetch_optional_mca_and_connector( if connector_name_or_mca_id.starts_with("mca_") { let mca = db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &state.into(), &merchant_account.merchant_id, connector_name_or_mca_id, key_store, diff --git a/crates/router/src/core/webhooks/outgoing.rs b/crates/router/src/core/webhooks/outgoing.rs index c40b4ae88696..ed2b41dc64d4 100644 --- a/crates/router/src/core/webhooks/outgoing.rs +++ b/crates/router/src/core/webhooks/outgoing.rs @@ -4,7 +4,7 @@ use api_models::{ webhook_events::{OutgoingWebhookRequestContent, OutgoingWebhookResponseContent}, webhooks, }; -use common_utils::{ext_traits::Encode, request::RequestContent}; +use common_utils::{ext_traits::Encode, request::RequestContent, types::keymanager::Identifier}; use diesel_models::process_tracker::business_status; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::type_encryption::decrypt; @@ -87,6 +87,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( }; let request_content = get_outgoing_webhook_request( + &state, &merchant_account, outgoing_webhook, &business_profile, @@ -97,7 +98,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( .attach_printable("Failed to construct outgoing webhook request content")?; let event_metadata = storage::EventMetadata::foreign_from((&content, &primary_object_id)); - + let key_manager_state = &(&state).into(); let new_event = domain::Event { event_id: event_id.clone(), event_type, @@ -113,11 +114,13 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( initial_attempt_id: Some(event_id.clone()), request: Some( domain_types::encrypt( + key_manager_state, request_content .encode_to_string_of_json() .change_context(errors::ApiErrorResponse::WebhookProcessingFailure) .attach_printable("Failed to encode outgoing webhook request content") .map(Secret::new)?, + Identifier::Merchant(merchant_key_store.merchant_id.clone()), merchant_key_store.key.get_inner().peek(), ) .await @@ -131,7 +134,7 @@ pub(crate) async fn create_event_and_trigger_outgoing_webhook( let event_insert_result = state .store - .insert_event(new_event, merchant_key_store) + .insert_event(key_manager_state, new_event, merchant_key_store) .await; let event = match event_insert_result { @@ -552,6 +555,7 @@ fn get_webhook_url_from_business_profile( } pub(crate) async fn get_outgoing_webhook_request( + state: &SessionState, merchant_account: &domain::MerchantAccount, outgoing_webhook: api::OutgoingWebhook, business_profile: &diesel_models::business_profile::BusinessProfile, @@ -559,6 +563,7 @@ pub(crate) async fn get_outgoing_webhook_request( ) -> CustomResult { #[inline] async fn get_outgoing_webhook_request_inner( + state: &SessionState, outgoing_webhook: api::OutgoingWebhook, business_profile: &diesel_models::business_profile::BusinessProfile, key_store: &domain::MerchantKeyStore, @@ -571,9 +576,11 @@ pub(crate) async fn get_outgoing_webhook_request( let transformed_outgoing_webhook = WebhookType::from(outgoing_webhook); let payment_response_hash_key = business_profile.payment_response_hash_key.clone(); let custom_headers = decrypt::( + &state.into(), business_profile .outgoing_webhook_custom_http_headers .clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -614,6 +621,7 @@ pub(crate) async fn get_outgoing_webhook_request( #[cfg(feature = "stripe")] Some(api_models::enums::Connector::Stripe) => { get_outgoing_webhook_request_inner::( + state, outgoing_webhook, business_profile, key_store, @@ -622,6 +630,7 @@ pub(crate) async fn get_outgoing_webhook_request( } _ => { get_outgoing_webhook_request_inner::( + state, outgoing_webhook, business_profile, key_store, @@ -645,7 +654,7 @@ async fn update_event_if_client_error( error_message: String, ) -> CustomResult { let is_webhook_notified = false; - + let key_manager_state = &(&state).into(); let response_to_store = OutgoingWebhookResponseContent { body: None, headers: None, @@ -657,12 +666,14 @@ async fn update_event_if_client_error( is_webhook_notified, response: Some( domain_types::encrypt( + key_manager_state, response_to_store .encode_to_string_of_json() .change_context( errors::WebhooksFlowError::OutgoingWebhookResponseEncodingFailed, ) .map(Secret::new)?, + Identifier::Merchant(merchant_key_store.merchant_id.clone()), merchant_key_store.key.get_inner().peek(), ) .await @@ -674,6 +685,7 @@ async fn update_event_if_client_error( state .store .update_event_by_merchant_id_event_id( + key_manager_state, merchant_id, event_id, event_update, @@ -733,7 +745,7 @@ async fn update_event_in_storage( ) -> CustomResult { let status_code = response.status(); let is_webhook_notified = status_code.is_success(); - + let key_manager_state = &(&state).into(); let response_headers = response .headers() .iter() @@ -772,12 +784,14 @@ async fn update_event_in_storage( is_webhook_notified, response: Some( domain_types::encrypt( + key_manager_state, response_to_store .encode_to_string_of_json() .change_context( errors::WebhooksFlowError::OutgoingWebhookResponseEncodingFailed, ) .map(Secret::new)?, + Identifier::Merchant(merchant_key_store.merchant_id.clone()), merchant_key_store.key.get_inner().peek(), ) .await @@ -788,6 +802,7 @@ async fn update_event_in_storage( state .store .update_event_by_merchant_id_event_id( + key_manager_state, merchant_id, event_id, event_update, diff --git a/crates/router/src/core/webhooks/webhook_events.rs b/crates/router/src/core/webhooks/webhook_events.rs index 2a16ccce0b40..19118ad639a2 100644 --- a/crates/router/src/core/webhooks/webhook_events.rs +++ b/crates/router/src/core/webhooks/webhook_events.rs @@ -28,7 +28,7 @@ pub async fn list_initial_delivery_attempts( api::webhook_events::EventListConstraintsInternal::foreign_try_from(constraints)?; let store = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let (account, key_store) = determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; @@ -36,14 +36,14 @@ pub async fn list_initial_delivery_attempts( api_models::webhook_events::EventListConstraintsInternal::ObjectIdFilter { object_id } => { match account { MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => store - .list_initial_events_by_merchant_id_primary_object_id( + .list_initial_events_by_merchant_id_primary_object_id(key_manager_state, &merchant_account.merchant_id, &object_id, &key_store, ) .await, MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => store - .list_initial_events_by_profile_id_primary_object_id( + .list_initial_events_by_profile_id_primary_object_id(key_manager_state, &business_profile.profile_id, &object_id, &key_store, @@ -73,7 +73,7 @@ pub async fn list_initial_delivery_attempts( match account { MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => store - .list_initial_events_by_merchant_id_constraints( + .list_initial_events_by_merchant_id_constraints(key_manager_state, &merchant_account.merchant_id, created_after, created_before, @@ -83,7 +83,7 @@ pub async fn list_initial_delivery_attempts( ) .await, MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => store - .list_initial_events_by_profile_id_constraints( + .list_initial_events_by_profile_id_constraints(key_manager_state, &business_profile.profile_id, created_after, created_before, @@ -116,11 +116,12 @@ pub async fn list_delivery_attempts( let (account, key_store) = determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; - + let key_manager_state = &(&state).into(); let events = match account { MerchantAccountOrBusinessProfile::MerchantAccount(merchant_account) => { store .list_events_by_merchant_id_initial_attempt_id( + key_manager_state, &merchant_account.merchant_id, &initial_attempt_id, &key_store, @@ -130,6 +131,7 @@ pub async fn list_delivery_attempts( MerchantAccountOrBusinessProfile::BusinessProfile(business_profile) => { store .list_events_by_profile_id_initial_attempt_id( + key_manager_state, &business_profile.profile_id, &initial_attempt_id, &key_store, @@ -162,12 +164,17 @@ pub async fn retry_delivery_attempt( event_id: String, ) -> RouterResponse { let store = state.store.as_ref(); - + let key_manager_state = &(&state).into(); let (account, key_store) = determine_identifier_and_get_key_store(state.clone(), merchant_id_or_profile_id).await?; let event_to_retry = store - .find_event_by_merchant_id_event_id(&key_store.merchant_id, &event_id, &key_store) + .find_event_by_merchant_id_event_id( + key_manager_state, + &key_store.merchant_id, + &event_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?; @@ -216,7 +223,7 @@ pub async fn retry_delivery_attempt( }; let event = store - .insert_event(new_event, &key_store) + .insert_event(key_manager_state, new_event, &key_store) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to insert event")?; @@ -245,7 +252,12 @@ pub async fn retry_delivery_attempt( .await; let updated_event = store - .find_event_by_merchant_id_event_id(&key_store.merchant_id, &new_event_id, &key_store) + .find_event_by_merchant_id_event_id( + key_manager_state, + &key_store.merchant_id, + &new_event_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::EventNotFound)?; @@ -259,8 +271,10 @@ async fn determine_identifier_and_get_key_store( merchant_id_or_profile_id: String, ) -> errors::RouterResult<(MerchantAccountOrBusinessProfile, domain::MerchantKeyStore)> { let store = state.store.as_ref(); + let key_manager_state = &(&state).into(); match store .get_merchant_key_store_by_merchant_id( + key_manager_state, &merchant_id_or_profile_id, &store.get_master_key().to_vec().into(), ) @@ -271,7 +285,11 @@ async fn determine_identifier_and_get_key_store( // Find a merchant account having `merchant_id` = `merchant_id_or_profile_id`. Ok(key_store) => { let merchant_account = store - .find_merchant_account_by_merchant_id(&merchant_id_or_profile_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &merchant_id_or_profile_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -301,6 +319,7 @@ async fn determine_identifier_and_get_key_store( let key_store = store .get_merchant_key_store_by_merchant_id( + key_manager_state, &business_profile.merchant_id, &store.get_master_key().to_vec().into(), ) diff --git a/crates/router/src/db/address.rs b/crates/router/src/db/address.rs index f210019b4dac..04a8f673a94e 100644 --- a/crates/router/src/db/address.rs +++ b/crates/router/src/db/address.rs @@ -1,4 +1,4 @@ -use common_utils::id_type; +use common_utils::{id_type, types::keymanager::KeyManagerState}; use diesel_models::{address::AddressUpdateInternal, enums::MerchantStorageScheme}; use error_stack::ResultExt; @@ -22,6 +22,7 @@ where { async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -29,6 +30,7 @@ where async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address: domain::AddressUpdate, payment_id: String, @@ -38,12 +40,14 @@ where async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn insert_address_for_payments( &self, + state: &KeyManagerState, payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -52,12 +56,14 @@ where async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -67,6 +73,7 @@ where async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, @@ -76,7 +83,7 @@ where #[cfg(not(feature = "kv_store"))] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -98,6 +105,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -107,7 +115,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -117,6 +129,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -134,7 +147,7 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }) @@ -144,6 +157,7 @@ mod storage { #[instrument(skip_all)] async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -154,7 +168,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -164,6 +182,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address_update: domain::AddressUpdate, _payment_id: String, @@ -180,7 +199,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -190,6 +213,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_payments( &self, + state: &KeyManagerState, _payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -205,7 +229,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -215,6 +243,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -228,7 +257,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -238,6 +271,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, @@ -257,7 +291,7 @@ mod storage { for address in addresses.into_iter() { output.push( address - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -271,7 +305,7 @@ mod storage { #[cfg(feature = "kv_store")] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use diesel_models::{enums::MerchantStorageScheme, AddressUpdateInternal}; use error_stack::{report, ResultExt}; use redis_interface::HsetnxReply; @@ -299,6 +333,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -308,7 +343,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -318,6 +357,7 @@ mod storage { #[instrument(skip_all)] async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -362,7 +402,11 @@ mod storage { } }?; address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -370,6 +414,7 @@ mod storage { #[instrument(skip_all)] async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -380,7 +425,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -390,6 +439,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address_update: domain::AddressUpdate, payment_id: String, @@ -420,7 +470,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -457,7 +511,11 @@ mod storage { .change_context(errors::StorageError::KVError)?; updated_address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -467,6 +525,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_payments( &self, + state: &KeyManagerState, payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -493,7 +552,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -553,7 +616,11 @@ mod storage { } .into()), Ok(HsetnxReply::KeySet) => Ok(created_address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?), Err(er) => Err(er).change_context(errors::StorageError::KVError), @@ -565,6 +632,7 @@ mod storage { #[instrument(skip_all)] async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -578,7 +646,11 @@ mod storage { .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|address| async { address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -588,6 +660,7 @@ mod storage { #[instrument(skip_all)] async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage_types::AddressUpdate, @@ -607,7 +680,11 @@ mod storage { for address in addresses.into_iter() { output.push( address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -623,6 +700,7 @@ mod storage { impl AddressInterface for MockDb { async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -635,7 +713,11 @@ impl AddressInterface for MockDb { { Some(address) => address .clone() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => { @@ -648,6 +730,7 @@ impl AddressInterface for MockDb { async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, _merchant_id: &str, _payment_id: &str, address_id: &str, @@ -663,7 +746,11 @@ impl AddressInterface for MockDb { { Some(address) => address .clone() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => { @@ -676,6 +763,7 @@ impl AddressInterface for MockDb { async fn update_address( &self, + state: &KeyManagerState, address_id: String, address_update: storage_types::AddressUpdate, key_store: &domain::MerchantKeyStore, @@ -694,7 +782,11 @@ impl AddressInterface for MockDb { }); match updated_addr { Some(address_updated) => address_updated - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => Err(errors::StorageError::ValueNotFound( @@ -706,6 +798,7 @@ impl AddressInterface for MockDb { async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address_update: domain::AddressUpdate, _payment_id: String, @@ -726,7 +819,11 @@ impl AddressInterface for MockDb { }); match updated_addr { Some(address_updated) => address_updated - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => Err(errors::StorageError::ValueNotFound( @@ -738,6 +835,7 @@ impl AddressInterface for MockDb { async fn insert_address_for_payments( &self, + state: &KeyManagerState, _payment_id: &str, address_new: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, @@ -752,13 +850,18 @@ impl AddressInterface for MockDb { addresses.push(address.clone()); address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn insert_address_for_customers( &self, + state: &KeyManagerState, address_new: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -771,13 +874,18 @@ impl AddressInterface for MockDb { addresses.push(address.clone()); address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address_update: storage_types::AddressUpdate, @@ -801,7 +909,11 @@ impl AddressInterface for MockDb { match updated_addr { Some(address) => { let address: domain::Address = address - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; Ok(vec![address]) diff --git a/crates/router/src/db/customers.rs b/crates/router/src/db/customers.rs index cad3d2969fe7..1bfda6585217 100644 --- a/crates/router/src/db/customers.rs +++ b/crates/router/src/db/customers.rs @@ -1,4 +1,4 @@ -use common_utils::{ext_traits::AsyncExt, id_type}; +use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use error_stack::ResultExt; use futures::future::try_join_all; use router_env::{instrument, tracing}; @@ -29,14 +29,17 @@ where async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::StorageError>; + #[allow(clippy::too_many_arguments)] async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, @@ -47,6 +50,7 @@ where async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -55,12 +59,14 @@ where async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -69,7 +75,7 @@ where #[cfg(feature = "kv_store")] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use diesel_models::kv; use error_stack::{report, ResultExt}; use futures::future::try_join_all; @@ -103,6 +109,7 @@ mod storage { // check customer not found in kv and fallback to db async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -149,9 +156,13 @@ mod storage { let maybe_result = maybe_customer .async_map(|c| async { - c.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await .transpose()?; @@ -167,6 +178,7 @@ mod storage { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, @@ -236,7 +248,11 @@ mod storage { }; updated_object? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -244,6 +260,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -287,7 +304,11 @@ mod storage { }?; let result: domain::Customer = customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; //.await @@ -303,6 +324,7 @@ mod storage { #[instrument(skip_all)] async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { @@ -316,7 +338,11 @@ mod storage { let customers = try_join_all(encrypted_customers.into_iter().map( |encrypted_customer| async { encrypted_customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }, @@ -329,6 +355,7 @@ mod storage { #[instrument(skip_all)] async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -394,7 +421,11 @@ mod storage { }?; create_customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -419,7 +450,7 @@ mod storage { #[cfg(not(feature = "kv_store"))] mod storage { - use common_utils::{ext_traits::AsyncExt, id_type}; + use common_utils::{ext_traits::AsyncExt, id_type, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use futures::future::try_join_all; use masking::PeekInterface; @@ -447,6 +478,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -462,7 +494,7 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error)))? .async_map(|c| async { - c.convert(key_store.key.get_inner()) + c.convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }) @@ -483,6 +515,7 @@ mod storage { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, _customer: domain::Customer, @@ -494,13 +527,13 @@ mod storage { storage_types::Customer::update_by_customer_id_merchant_id( &conn, customer_id, - merchant_id, + merchant_id.clone(), customer_update.into(), ) .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|c| async { - c.convert(key_store.key.get_inner()) + c.convert(state, key_store.key.get_inner(), merchant_id) .await .change_context(errors::StorageError::DecryptionError) }) @@ -510,6 +543,7 @@ mod storage { #[instrument(skip_all)] async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -525,7 +559,7 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|c| async { - c.convert(key_store.key.get_inner()) + c.convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }) @@ -541,6 +575,7 @@ mod storage { #[instrument(skip_all)] async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { @@ -554,7 +589,7 @@ mod storage { let customers = try_join_all(encrypted_customers.into_iter().map( |encrypted_customer| async { encrypted_customer - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) }, @@ -567,6 +602,7 @@ mod storage { #[instrument(skip_all)] async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -580,9 +616,13 @@ mod storage { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|c| async { - c.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -610,6 +650,7 @@ impl CustomerInterface for MockDb { #[allow(clippy::panic)] async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -624,9 +665,13 @@ impl CustomerInterface for MockDb { .cloned(); customer .async_map(|c| async { - c.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + c.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await .transpose() @@ -634,6 +679,7 @@ impl CustomerInterface for MockDb { async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { @@ -646,7 +692,11 @@ impl CustomerInterface for MockDb { .map(|customer| async { customer .to_owned() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }), @@ -659,6 +709,7 @@ impl CustomerInterface for MockDb { #[instrument(skip_all)] async fn update_customer_by_customer_id_merchant_id( &self, + _state: &KeyManagerState, _customer_id: id_type::CustomerId, _merchant_id: String, _customer: domain::Customer, @@ -672,6 +723,7 @@ impl CustomerInterface for MockDb { async fn find_customer_by_customer_id_merchant_id( &self, + _state: &KeyManagerState, _customer_id: &id_type::CustomerId, _merchant_id: &str, _key_store: &domain::MerchantKeyStore, @@ -684,6 +736,7 @@ impl CustomerInterface for MockDb { #[allow(clippy::panic)] async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -697,7 +750,11 @@ impl CustomerInterface for MockDb { customers.push(customer.clone()); customer - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } diff --git a/crates/router/src/db/events.rs b/crates/router/src/db/events.rs index aba11f959885..2fd8a09f1733 100644 --- a/crates/router/src/db/events.rs +++ b/crates/router/src/db/events.rs @@ -1,4 +1,4 @@ -use common_utils::ext_traits::AsyncExt; +use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -23,12 +23,14 @@ where { async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -36,13 +38,16 @@ where async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; + #[allow(clippy::too_many_arguments)] async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -53,6 +58,7 @@ where async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -60,13 +66,16 @@ where async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError>; + #[allow(clippy::too_many_arguments)] async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -77,6 +86,7 @@ where async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -84,6 +94,7 @@ where async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, @@ -96,6 +107,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -107,7 +119,11 @@ impl EventInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -115,6 +131,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -123,7 +140,11 @@ impl EventInterface for Store { storage::Event::find_by_merchant_id_event_id(&conn, merchant_id, event_id) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -131,6 +152,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -148,7 +170,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -161,6 +187,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -184,7 +211,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -197,6 +228,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -214,7 +246,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -227,6 +263,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -244,7 +281,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -257,6 +298,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -280,7 +322,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -293,6 +339,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -306,7 +353,11 @@ impl EventInterface for Store { for event in events.into_iter() { domain_events.push( event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ); @@ -319,6 +370,7 @@ impl EventInterface for Store { #[instrument(skip_all)] async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, @@ -328,7 +380,11 @@ impl EventInterface for Store { storage::Event::update_by_merchant_id_event_id(&conn, merchant_id, event_id, event.into()) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -338,6 +394,7 @@ impl EventInterface for Store { impl EventInterface for MockDb { async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -350,13 +407,18 @@ impl EventInterface for MockDb { locked_events.push(stored_event.clone()); stored_event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -370,7 +432,11 @@ impl EventInterface for MockDb { .cloned() .async_map(|event| async { event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -386,6 +452,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -405,7 +472,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -416,6 +487,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -470,7 +542,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -481,6 +557,7 @@ impl EventInterface for MockDb { async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -498,7 +575,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -509,6 +590,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -528,7 +610,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -539,6 +625,7 @@ impl EventInterface for MockDb { async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -593,7 +680,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -604,6 +695,7 @@ impl EventInterface for MockDb { async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, @@ -621,7 +713,11 @@ impl EventInterface for MockDb { for event in events { let domain_event = event - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?; domain_events.push(domain_event); @@ -632,6 +728,7 @@ impl EventInterface for MockDb { async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, @@ -657,7 +754,11 @@ impl EventInterface for MockDb { event_to_update .clone() - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -665,6 +766,9 @@ impl EventInterface for MockDb { #[cfg(test)] mod tests { + use std::sync::Arc; + + use common_utils::types::keymanager::Identifier; use diesel_models::{enums, events::EventMetadata}; use time::macros::datetime; @@ -673,6 +777,10 @@ mod tests { events::EventInterface, merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb, }, + routes::{ + self, + app::{settings::Settings, StorageImpl}, + }, services, types::domain, }; @@ -685,17 +793,31 @@ mod tests { .await .expect("Failed to create Mock store"); let event_id = "test_event_id"; + let (tx, _) = tokio::sync::oneshot::channel(); + let app_state = Box::pin(routes::AppState::with_storage( + Settings::default(), + StorageImpl::PostgresqlTest, + tx, + Box::new(services::MockApiClient), + )) + .await; + let state = &Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); let merchant_id = "merchant1"; let business_profile_id = "profile1"; let payment_id = "test_payment_id"; - + let key_manager_state = &state.into(); let master_key = mockdb.get_master_key(); mockdb .insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + Identifier::Merchant(merchant_id.to_string()), master_key, ) .await @@ -707,12 +829,17 @@ mod tests { .await .unwrap(); let merchant_key_store = mockdb - .get_merchant_key_store_by_merchant_id(merchant_id, &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &master_key.to_vec().into(), + ) .await .unwrap(); let event1 = mockdb .insert_event( + key_manager_state, domain::Event { event_id: event_id.into(), event_type: enums::EventType::PaymentSucceeded, @@ -742,6 +869,7 @@ mod tests { let updated_event = mockdb .update_event_by_merchant_id_event_id( + key_manager_state, merchant_id, event_id, domain::EventUpdate::UpdateResponse { diff --git a/crates/router/src/db/kafka_store.rs b/crates/router/src/db/kafka_store.rs index ce0821c6322f..ed2357c1a1ca 100644 --- a/crates/router/src/db/kafka_store.rs +++ b/crates/router/src/db/kafka_store.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use common_enums::enums::MerchantStorageScheme; -use common_utils::{errors::CustomResult, id_type, pii}; +use common_utils::{errors::CustomResult, id_type, pii, types::keymanager::KeyManagerState}; use diesel_models::{ enums, enums::ProcessTrackerStatus, @@ -102,27 +102,30 @@ impl KafkaStore { impl AddressInterface for KafkaStore { async fn find_address_by_address_id( &self, + state: &KeyManagerState, address_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .find_address_by_address_id(address_id, key_store) + .find_address_by_address_id(state, address_id, key_store) .await } async fn update_address( &self, + state: &KeyManagerState, address_id: String, address: storage::AddressUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_address(address_id, address, key_store) + .update_address(state, address_id, address, key_store) .await } async fn update_address_for_payments( &self, + state: &KeyManagerState, this: domain::PaymentAddress, address: domain::AddressUpdate, payment_id: String, @@ -130,24 +133,33 @@ impl AddressInterface for KafkaStore { storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store - .update_address_for_payments(this, address, payment_id, key_store, storage_scheme) + .update_address_for_payments( + state, + this, + address, + payment_id, + key_store, + storage_scheme, + ) .await } async fn insert_address_for_payments( &self, + state: &KeyManagerState, payment_id: &str, address: domain::PaymentAddress, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store - .insert_address_for_payments(payment_id, address, key_store, storage_scheme) + .insert_address_for_payments(state, payment_id, address, key_store, storage_scheme) .await } async fn find_address_by_merchant_id_payment_id_address_id( &self, + state: &KeyManagerState, merchant_id: &str, payment_id: &str, address_id: &str, @@ -156,6 +168,7 @@ impl AddressInterface for KafkaStore { ) -> CustomResult { self.diesel_store .find_address_by_merchant_id_payment_id_address_id( + state, merchant_id, payment_id, address_id, @@ -167,23 +180,31 @@ impl AddressInterface for KafkaStore { async fn insert_address_for_customers( &self, + state: &KeyManagerState, address: domain::CustomerAddress, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_address_for_customers(address, key_store) + .insert_address_for_customers(state, address, key_store) .await } async fn update_address_by_merchant_id_customer_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, address: storage::AddressUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store - .update_address_by_merchant_id_customer_id(customer_id, merchant_id, address, key_store) + .update_address_by_merchant_id_customer_id( + state, + customer_id, + merchant_id, + address, + key_store, + ) .await } } @@ -332,6 +353,7 @@ impl CustomerInterface for KafkaStore { async fn find_customer_optional_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -339,6 +361,7 @@ impl CustomerInterface for KafkaStore { ) -> CustomResult, errors::StorageError> { self.diesel_store .find_customer_optional_by_customer_id_merchant_id( + state, customer_id, merchant_id, key_store, @@ -349,6 +372,7 @@ impl CustomerInterface for KafkaStore { async fn update_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: id_type::CustomerId, merchant_id: String, customer: domain::Customer, @@ -358,6 +382,7 @@ impl CustomerInterface for KafkaStore { ) -> CustomResult { self.diesel_store .update_customer_by_customer_id_merchant_id( + state, customer_id, merchant_id, customer, @@ -370,16 +395,18 @@ impl CustomerInterface for KafkaStore { async fn list_customers_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_customers_by_merchant_id(merchant_id, key_store) + .list_customers_by_merchant_id(state, merchant_id, key_store) .await } async fn find_customer_by_customer_id_merchant_id( &self, + state: &KeyManagerState, customer_id: &id_type::CustomerId, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -387,6 +414,7 @@ impl CustomerInterface for KafkaStore { ) -> CustomResult { self.diesel_store .find_customer_by_customer_id_merchant_id( + state, customer_id, merchant_id, key_store, @@ -397,12 +425,13 @@ impl CustomerInterface for KafkaStore { async fn insert_customer( &self, + state: &KeyManagerState, customer_data: domain::Customer, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult { self.diesel_store - .insert_customer(customer_data, key_store, storage_scheme) + .insert_customer(state, customer_data, key_store, storage_scheme) .await } } @@ -519,33 +548,37 @@ impl EphemeralKeyInterface for KafkaStore { impl EventInterface for KafkaStore { async fn insert_event( &self, + state: &KeyManagerState, event: domain::Event, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_event(event, merchant_key_store) + .insert_event(state, event, merchant_key_store) .await } async fn find_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .find_event_by_merchant_id_event_id(merchant_id, event_id, merchant_key_store) + .find_event_by_merchant_id_event_id(state, merchant_id, event_id, merchant_key_store) .await } async fn list_initial_events_by_merchant_id_primary_object_id( &self, + state: &KeyManagerState, merchant_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_merchant_id_primary_object_id( + state, merchant_id, primary_object_id, merchant_key_store, @@ -555,6 +588,7 @@ impl EventInterface for KafkaStore { async fn list_initial_events_by_merchant_id_constraints( &self, + state: &KeyManagerState, merchant_id: &str, created_after: Option, created_before: Option, @@ -564,6 +598,7 @@ impl EventInterface for KafkaStore { ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_merchant_id_constraints( + state, merchant_id, created_after, created_before, @@ -576,12 +611,14 @@ impl EventInterface for KafkaStore { async fn list_events_by_merchant_id_initial_attempt_id( &self, + state: &KeyManagerState, merchant_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_events_by_merchant_id_initial_attempt_id( + state, merchant_id, initial_attempt_id, merchant_key_store, @@ -591,12 +628,14 @@ impl EventInterface for KafkaStore { async fn list_initial_events_by_profile_id_primary_object_id( &self, + state: &KeyManagerState, profile_id: &str, primary_object_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_profile_id_primary_object_id( + state, profile_id, primary_object_id, merchant_key_store, @@ -606,6 +645,7 @@ impl EventInterface for KafkaStore { async fn list_initial_events_by_profile_id_constraints( &self, + state: &KeyManagerState, profile_id: &str, created_after: Option, created_before: Option, @@ -615,6 +655,7 @@ impl EventInterface for KafkaStore { ) -> CustomResult, errors::StorageError> { self.diesel_store .list_initial_events_by_profile_id_constraints( + state, profile_id, created_after, created_before, @@ -627,12 +668,14 @@ impl EventInterface for KafkaStore { async fn list_events_by_profile_id_initial_attempt_id( &self, + state: &KeyManagerState, profile_id: &str, initial_attempt_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .list_events_by_profile_id_initial_attempt_id( + state, profile_id, initial_attempt_id, merchant_key_store, @@ -642,13 +685,20 @@ impl EventInterface for KafkaStore { async fn update_event_by_merchant_id_event_id( &self, + state: &KeyManagerState, merchant_id: &str, event_id: &str, event: domain::EventUpdate, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_event_by_merchant_id_event_id(merchant_id, event_id, event, merchant_key_store) + .update_event_by_merchant_id_event_id( + state, + merchant_id, + event_id, + event, + merchant_key_store, + ) .await } } @@ -790,43 +840,47 @@ impl PaymentLinkInterface for KafkaStore { impl MerchantAccountInterface for KafkaStore { async fn insert_merchant( &self, + state: &KeyManagerState, merchant_account: domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_merchant(merchant_account, key_store) + .insert_merchant(state, merchant_account, key_store) .await } async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .find_merchant_account_by_merchant_id(merchant_id, key_store) + .find_merchant_account_by_merchant_id(state, merchant_id, key_store) .await } async fn update_merchant( &self, + state: &KeyManagerState, this: domain::MerchantAccount, merchant_account: storage::MerchantAccountUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_merchant(this, merchant_account, key_store) + .update_merchant(state, this, merchant_account, key_store) .await } async fn update_specific_fields_in_merchant( &self, + state: &KeyManagerState, merchant_id: &str, merchant_account: storage::MerchantAccountUpdate, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_specific_fields_in_merchant(merchant_id, merchant_account, key_store) + .update_specific_fields_in_merchant(state, merchant_id, merchant_account, key_store) .await } @@ -841,20 +895,22 @@ impl MerchantAccountInterface for KafkaStore { async fn find_merchant_account_by_publishable_key( &self, + state: &KeyManagerState, publishable_key: &str, ) -> CustomResult { self.diesel_store - .find_merchant_account_by_publishable_key(publishable_key) + .find_merchant_account_by_publishable_key(state, publishable_key) .await } #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( &self, + state: &KeyManagerState, organization_id: &str, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_merchant_accounts_by_organization_id(organization_id) + .list_merchant_accounts_by_organization_id(state, organization_id) .await } @@ -870,10 +926,11 @@ impl MerchantAccountInterface for KafkaStore { #[cfg(feature = "olap")] async fn list_multiple_merchant_accounts( &self, + state: &KeyManagerState, merchant_ids: Vec, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_multiple_merchant_accounts(merchant_ids) + .list_multiple_merchant_accounts(state, merchant_ids) .await } } @@ -957,12 +1014,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { } async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store .find_merchant_connector_account_by_merchant_id_connector_label( + state, merchant_id, connector, key_store, @@ -972,12 +1031,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .find_merchant_connector_account_by_merchant_id_connector_name( + state, merchant_id, connector_name, key_store, @@ -987,12 +1048,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store .find_merchant_connector_account_by_profile_id_connector_name( + state, profile_id, connector_name, key_store, @@ -1002,22 +1065,25 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .insert_merchant_connector_account(t, key_store) + .insert_merchant_connector_account(state, t, key_store) .await } async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + state, merchant_id, merchant_connector_id, key_store, @@ -1027,12 +1093,14 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, ) -> CustomResult, errors::StorageError> { self.diesel_store .find_merchant_connector_account_by_merchant_id_and_disabled_list( + state, merchant_id, get_disabled, key_store, @@ -1042,12 +1110,13 @@ impl MerchantConnectorAccountInterface for KafkaStore { async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, ) -> CustomResult { self.diesel_store - .update_merchant_connector_account(this, merchant_connector_account, key_store) + .update_merchant_connector_account(state, this, merchant_connector_account, key_store) .await } @@ -1326,6 +1395,7 @@ impl PaymentAttemptInterface for KafkaStore { impl PaymentIntentInterface for KafkaStore { async fn update_payment_intent( &self, + state: &KeyManagerState, this: storage::PaymentIntent, payment_intent: storage::PaymentIntentUpdate, key_store: &domain::MerchantKeyStore, @@ -1333,7 +1403,13 @@ impl PaymentIntentInterface for KafkaStore { ) -> CustomResult { let intent = self .diesel_store - .update_payment_intent(this.clone(), payment_intent, key_store, storage_scheme) + .update_payment_intent( + state, + this.clone(), + payment_intent, + key_store, + storage_scheme, + ) .await?; if let Err(er) = self @@ -1349,6 +1425,7 @@ impl PaymentIntentInterface for KafkaStore { async fn insert_payment_intent( &self, + state: &KeyManagerState, new: storage::PaymentIntent, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -1356,7 +1433,7 @@ impl PaymentIntentInterface for KafkaStore { logger::debug!("Inserting PaymentIntent Via KafkaStore"); let intent = self .diesel_store - .insert_payment_intent(new, key_store, storage_scheme) + .insert_payment_intent(state, new, key_store, storage_scheme) .await?; if let Err(er) = self @@ -1372,6 +1449,7 @@ impl PaymentIntentInterface for KafkaStore { async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, key_store: &domain::MerchantKeyStore, @@ -1379,6 +1457,7 @@ impl PaymentIntentInterface for KafkaStore { ) -> CustomResult { self.diesel_store .find_payment_intent_by_payment_id_merchant_id( + state, payment_id, merchant_id, key_store, @@ -1390,19 +1469,27 @@ impl PaymentIntentInterface for KafkaStore { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, key_store: &domain::MerchantKeyStore, storage_scheme: MerchantStorageScheme, ) -> CustomResult, errors::DataStorageError> { self.diesel_store - .filter_payment_intent_by_constraints(merchant_id, filters, key_store, storage_scheme) + .filter_payment_intent_by_constraints( + state, + merchant_id, + filters, + key_store, + storage_scheme, + ) .await } #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, key_store: &domain::MerchantKeyStore, @@ -1410,6 +1497,7 @@ impl PaymentIntentInterface for KafkaStore { ) -> CustomResult, errors::DataStorageError> { self.diesel_store .filter_payment_intents_by_time_range_constraints( + state, merchant_id, time_range, key_store, @@ -1421,6 +1509,7 @@ impl PaymentIntentInterface for KafkaStore { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, key_store: &domain::MerchantKeyStore, @@ -1434,6 +1523,7 @@ impl PaymentIntentInterface for KafkaStore { > { self.diesel_store .get_filtered_payment_intents_attempt( + state, merchant_id, constraints, key_store, @@ -2059,21 +2149,23 @@ impl RefundInterface for KafkaStore { impl MerchantKeyStoreInterface for KafkaStore { async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult { self.diesel_store - .insert_merchant_key_store(merchant_key_store, key) + .insert_merchant_key_store(state, merchant_key_store, key) .await } async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult { self.diesel_store - .get_merchant_key_store_by_merchant_id(merchant_id, key) + .get_merchant_key_store_by_merchant_id(state, merchant_id, key) .await } @@ -2089,18 +2181,20 @@ impl MerchantKeyStoreInterface for KafkaStore { #[cfg(feature = "olap")] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError> { self.diesel_store - .list_multiple_key_stores(merchant_ids, key) + .list_multiple_key_stores(state, merchant_ids, key) .await } async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { - self.diesel_store.get_all_key_stores(key).await + self.diesel_store.get_all_key_stores(state, key).await } } @@ -2592,6 +2686,7 @@ impl DashboardMetadataInterface for KafkaStore { impl BatchSampleDataInterface for KafkaStore { async fn insert_payment_intents_batch_for_sample_data( &self, + state: &KeyManagerState, batch: Vec, key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore, ) -> CustomResult< @@ -2600,7 +2695,7 @@ impl BatchSampleDataInterface for KafkaStore { > { let payment_intents_list = self .diesel_store - .insert_payment_intents_batch_for_sample_data(batch, key_store) + .insert_payment_intents_batch_for_sample_data(state, batch, key_store) .await?; for payment_intent in payment_intents_list.iter() { @@ -2654,6 +2749,7 @@ impl BatchSampleDataInterface for KafkaStore { async fn delete_payment_intents_for_sample_data( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &hyperswitch_domain_models::merchant_key_store::MerchantKeyStore, ) -> CustomResult< @@ -2662,7 +2758,7 @@ impl BatchSampleDataInterface for KafkaStore { > { let payment_intents_list = self .diesel_store - .delete_payment_intents_for_sample_data(merchant_id, key_store) + .delete_payment_intents_for_sample_data(state, merchant_id, key_store) .await?; for payment_intent in payment_intents_list.iter() { @@ -2947,29 +3043,32 @@ impl GenericLinkInterface for KafkaStore { impl UserKeyStoreInterface for KafkaStore { async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult { self.diesel_store - .insert_user_key_store(user_key_store, key) + .insert_user_key_store(state, user_key_store, key) .await } async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult { self.diesel_store - .get_user_key_store_by_user_id(user_id, key) + .get_user_key_store_by_user_id(state, user_id, key) .await } async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { - self.diesel_store.get_all_user_key_store(key).await + self.diesel_store.get_all_user_key_store(state, key).await } } diff --git a/crates/router/src/db/merchant_account.rs b/crates/router/src/db/merchant_account.rs index ff3830b23810..eadf00c872c8 100644 --- a/crates/router/src/db/merchant_account.rs +++ b/crates/router/src/db/merchant_account.rs @@ -1,7 +1,7 @@ #[cfg(feature = "olap")] use std::collections::HashMap; -use common_utils::ext_traits::AsyncExt; +use common_utils::{ext_traits::AsyncExt, types::keymanager::KeyManagerState}; use diesel_models::MerchantAccountUpdateInternal; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; @@ -31,12 +31,14 @@ where { async fn insert_merchant( &self, + state: &KeyManagerState, merchant_account: domain::MerchantAccount, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult; @@ -48,6 +50,7 @@ where async fn update_merchant( &self, + state: &KeyManagerState, this: domain::MerchantAccount, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -55,6 +58,7 @@ where async fn update_specific_fields_in_merchant( &self, + state: &KeyManagerState, merchant_id: &str, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -62,12 +66,14 @@ where async fn find_merchant_account_by_publishable_key( &self, + state: &KeyManagerState, publishable_key: &str, ) -> CustomResult; #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( &self, + state: &KeyManagerState, organization_id: &str, ) -> CustomResult, errors::StorageError>; @@ -79,6 +85,7 @@ where #[cfg(feature = "olap")] async fn list_multiple_merchant_accounts( &self, + state: &KeyManagerState, merchant_ids: Vec, ) -> CustomResult, errors::StorageError>; } @@ -88,6 +95,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn insert_merchant( &self, + state: &KeyManagerState, merchant_account: domain::MerchantAccount, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -99,7 +107,11 @@ impl MerchantAccountInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -107,6 +119,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -121,7 +134,11 @@ impl MerchantAccountInterface for Store { { fetch_func() .await? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_id.to_string(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -130,7 +147,11 @@ impl MerchantAccountInterface for Store { { cache::get_or_populate_in_memory(self, merchant_id, fetch_func, &ACCOUNTS_CACHE) .await? - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -139,6 +160,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn update_merchant( &self, + state: &KeyManagerState, this: domain::MerchantAccount, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -157,7 +179,11 @@ impl MerchantAccountInterface for Store { publish_and_redact_merchant_account_cache(self, &updated_merchant_account).await?; } updated_merchant_account - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -165,6 +191,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn update_specific_fields_in_merchant( &self, + state: &KeyManagerState, merchant_id: &str, merchant_account: storage::MerchantAccountUpdate, merchant_key_store: &domain::MerchantKeyStore, @@ -183,7 +210,11 @@ impl MerchantAccountInterface for Store { publish_and_redact_merchant_account_cache(self, &updated_merchant_account).await?; } updated_merchant_account - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -191,6 +222,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_account_by_publishable_key( &self, + state: &KeyManagerState, publishable_key: &str, ) -> CustomResult { let fetch_by_pub_key_func = || async { @@ -219,6 +251,7 @@ impl MerchantAccountInterface for Store { } let key_store = self .get_merchant_key_store_by_merchant_id( + state, &merchant_account.merchant_id, &self.get_master_key().to_vec().into(), ) @@ -226,7 +259,11 @@ impl MerchantAccountInterface for Store { Ok(authentication::AuthenticationData { merchant_account: merchant_account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, @@ -238,6 +275,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn list_merchant_accounts_by_organization_id( &self, + state: &KeyManagerState, organization_id: &str, ) -> CustomResult, errors::StorageError> { use futures::future::try_join_all; @@ -253,6 +291,7 @@ impl MerchantAccountInterface for Store { let merchant_key_stores = try_join_all(encrypted_merchant_accounts.iter().map(|merchant_account| { self.get_merchant_key_store_by_merchant_id( + state, &merchant_account.merchant_id, &db_master_key, ) @@ -265,7 +304,11 @@ impl MerchantAccountInterface for Store { .zip(merchant_key_stores.iter()) .map(|(merchant_account, key_store)| async { merchant_account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }), @@ -314,6 +357,7 @@ impl MerchantAccountInterface for Store { #[instrument(skip_all)] async fn list_multiple_merchant_accounts( &self, + state: &KeyManagerState, merchant_ids: Vec, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -327,6 +371,7 @@ impl MerchantAccountInterface for Store { let merchant_key_stores = self .list_multiple_key_stores( + state, encrypted_merchant_accounts .iter() .map(|merchant_account| &merchant_account.merchant_id) @@ -351,7 +396,11 @@ impl MerchantAccountInterface for Store { )), )?; merchant_account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }, @@ -399,6 +448,7 @@ impl MerchantAccountInterface for MockDb { #[allow(clippy::panic)] async fn insert_merchant( &self, + state: &KeyManagerState, mut merchant_account: domain::MerchantAccount, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -412,7 +462,11 @@ impl MerchantAccountInterface for MockDb { accounts.push(account.clone()); account - .convert(merchant_key_store.key.get_inner()) + .convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -420,6 +474,7 @@ impl MerchantAccountInterface for MockDb { #[allow(clippy::panic)] async fn find_merchant_account_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -429,9 +484,13 @@ impl MerchantAccountInterface for MockDb { .find(|account| account.merchant_id == merchant_id) .cloned() .async_map(|a| async { - a.convert(merchant_key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + a.convert( + state, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await .transpose()?; @@ -445,6 +504,7 @@ impl MerchantAccountInterface for MockDb { async fn update_merchant( &self, + _state: &KeyManagerState, _this: domain::MerchantAccount, _merchant_account: storage::MerchantAccountUpdate, _merchant_key_store: &domain::MerchantKeyStore, @@ -455,6 +515,7 @@ impl MerchantAccountInterface for MockDb { async fn update_specific_fields_in_merchant( &self, + _state: &KeyManagerState, _merchant_id: &str, _merchant_account: storage::MerchantAccountUpdate, _merchant_key_store: &domain::MerchantKeyStore, @@ -465,6 +526,7 @@ impl MerchantAccountInterface for MockDb { async fn find_merchant_account_by_publishable_key( &self, + _state: &KeyManagerState, _publishable_key: &str, ) -> CustomResult { // [#172]: Implement function for `MockDb` @@ -489,6 +551,7 @@ impl MerchantAccountInterface for MockDb { #[cfg(feature = "olap")] async fn list_merchant_accounts_by_organization_id( &self, + _state: &KeyManagerState, _organization_id: &str, ) -> CustomResult, errors::StorageError> { Err(errors::StorageError::MockDbError)? @@ -497,6 +560,7 @@ impl MerchantAccountInterface for MockDb { #[cfg(feature = "olap")] async fn list_multiple_merchant_accounts( &self, + _state: &KeyManagerState, _merchant_ids: Vec, ) -> CustomResult, errors::StorageError> { Err(errors::StorageError::MockDbError)? diff --git a/crates/router/src/db/merchant_connector_account.rs b/crates/router/src/db/merchant_connector_account.rs index b9becb90dcab..078980f91842 100644 --- a/crates/router/src/db/merchant_connector_account.rs +++ b/crates/router/src/db/merchant_connector_account.rs @@ -1,6 +1,9 @@ use async_bb8_diesel::AsyncConnection; -use common_utils::ext_traits::{AsyncExt, ByteSliceExt, Encode}; -use diesel_models::encryption::Encryption; +use common_utils::{ + encryption::Encryption, + ext_traits::{AsyncExt, ByteSliceExt, Encode}, + types::keymanager::KeyManagerState, +}; use error_stack::{report, ResultExt}; use router_env::{instrument, tracing}; #[cfg(feature = "accounts_cache")] @@ -121,6 +124,7 @@ where { async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector_label: &str, key_store: &domain::MerchantKeyStore, @@ -128,6 +132,7 @@ where async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -135,6 +140,7 @@ where async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -142,12 +148,14 @@ where async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult; async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, @@ -155,6 +163,7 @@ where async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, @@ -162,6 +171,7 @@ where async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, @@ -187,6 +197,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector_label: &str, key_store: &domain::MerchantKeyStore, @@ -206,7 +217,7 @@ impl MerchantConnectorAccountInterface for Store { { find_call() .await? - .convert(key_store.key.get_inner()) + .convert(state, key_store.key.get_inner(), merchant_id.to_string()) .await .change_context(errors::StorageError::DeserializationFailed) } @@ -221,9 +232,13 @@ impl MerchantConnectorAccountInterface for Store { ) .await .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -232,6 +247,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -251,7 +267,11 @@ impl MerchantConnectorAccountInterface for Store { { find_call() .await? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DeserializationFailed) } @@ -266,9 +286,13 @@ impl MerchantConnectorAccountInterface for Store { ) .await .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -277,6 +301,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -293,9 +318,13 @@ impl MerchantConnectorAccountInterface for Store { let mut output = Vec::with_capacity(items.len()); for item in items.into_iter() { output.push( - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError)?, + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?, ) } Ok(output) @@ -306,6 +335,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, @@ -325,7 +355,11 @@ impl MerchantConnectorAccountInterface for Store { { find_call() .await? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -339,7 +373,11 @@ impl MerchantConnectorAccountInterface for Store { &cache::ACCOUNTS_CACHE, ) .await? - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } @@ -348,6 +386,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -359,9 +398,13 @@ impl MerchantConnectorAccountInterface for Store { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await } @@ -369,6 +412,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, @@ -381,9 +425,13 @@ impl MerchantConnectorAccountInterface for Store { let mut output = Vec::with_capacity(items.len()); for item in items.into_iter() { output.push( - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError)?, + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError)?, ) } Ok(output) @@ -492,6 +540,7 @@ impl MerchantConnectorAccountInterface for Store { #[instrument(skip_all)] async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, @@ -516,9 +565,13 @@ impl MerchantConnectorAccountInterface for Store { .await .map_err(|error| report!(errors::StorageError::from(error))) .async_and_then(|item| async { - item.convert(key_store.key.get_inner()) - .await - .change_context(errors::StorageError::DecryptionError) + item.convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) + .await + .change_context(errors::StorageError::DecryptionError) }) .await }; @@ -629,6 +682,7 @@ impl MerchantConnectorAccountInterface for MockDb { } async fn find_merchant_connector_account_by_merchant_id_connector_label( &self, + state: &KeyManagerState, merchant_id: &str, connector: &str, key_store: &domain::MerchantKeyStore, @@ -645,7 +699,11 @@ impl MerchantConnectorAccountInterface for MockDb { .cloned() .async_map(|account| async { account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -663,6 +721,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn find_merchant_connector_account_by_merchant_id_connector_name( &self, + state: &KeyManagerState, merchant_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -681,7 +740,11 @@ impl MerchantConnectorAccountInterface for MockDb { for account in accounts.into_iter() { output.push( account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -691,6 +754,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn find_merchant_connector_account_by_profile_id_connector_name( &self, + state: &KeyManagerState, profile_id: &str, connector_name: &str, key_store: &domain::MerchantKeyStore, @@ -709,7 +773,11 @@ impl MerchantConnectorAccountInterface for MockDb { match maybe_mca { Some(mca) => mca .to_owned() - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError), None => Err(errors::StorageError::ValueNotFound( @@ -721,6 +789,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn find_by_merchant_connector_account_merchant_id_merchant_connector_id( &self, + state: &KeyManagerState, merchant_id: &str, merchant_connector_id: &str, key_store: &domain::MerchantKeyStore, @@ -737,7 +806,11 @@ impl MerchantConnectorAccountInterface for MockDb { .cloned() .async_map(|account| async { account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -755,6 +828,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn insert_merchant_connector_account( &self, + state: &KeyManagerState, t: domain::MerchantConnectorAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { @@ -788,13 +862,18 @@ impl MerchantConnectorAccountInterface for MockDb { }; accounts.push(account.clone()); account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) } async fn find_merchant_connector_account_by_merchant_id_and_disabled_list( &self, + state: &KeyManagerState, merchant_id: &str, get_disabled: bool, key_store: &domain::MerchantKeyStore, @@ -818,7 +897,11 @@ impl MerchantConnectorAccountInterface for MockDb { for account in accounts.into_iter() { output.push( account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError)?, ) @@ -828,6 +911,7 @@ impl MerchantConnectorAccountInterface for MockDb { async fn update_merchant_connector_account( &self, + state: &KeyManagerState, this: domain::MerchantConnectorAccount, merchant_connector_account: storage::MerchantConnectorAccountUpdateInternal, key_store: &domain::MerchantKeyStore, @@ -846,7 +930,11 @@ impl MerchantConnectorAccountInterface for MockDb { }) .async_map(|account| async { account - .convert(key_store.key.get_inner()) + .convert( + state, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) .await .change_context(errors::StorageError::DecryptionError) }) @@ -890,8 +978,10 @@ impl MerchantConnectorAccountInterface for MockDb { #[cfg(feature = "accounts_cache")] #[cfg(test)] mod merchant_connector_account_cache_tests { + use std::sync::Arc; + use api_models::enums::CountryAlpha2; - use common_utils::date_time; + use common_utils::{date_time, types::keymanager::Identifier}; use diesel_models::enums::ConnectorType; use error_stack::ResultExt; use masking::PeekInterface; @@ -901,6 +991,7 @@ mod merchant_connector_account_cache_tests { pub_sub::PubSubInterface, }; use time::macros::datetime; + use tokio::sync::oneshot; use crate::{ core::errors, @@ -908,6 +999,10 @@ mod merchant_connector_account_cache_tests { merchant_connector_account::MerchantConnectorAccountInterface, merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb, }, + routes::{ + self, + app::{settings::Settings, StorageImpl}, + }, services, types::{ domain::{self, behaviour::Conversion}, @@ -918,6 +1013,19 @@ mod merchant_connector_account_cache_tests { #[allow(clippy::unwrap_used)] #[tokio::test] async fn test_connector_profile_id_cache() { + let conf = Settings::new().unwrap(); + let tx: oneshot::Sender<()> = oneshot::channel().0; + + let app_state = Box::pin(routes::AppState::with_storage( + conf, + StorageImpl::PostgresqlTest, + tx, + Box::new(services::MockApiClient), + )) + .await; + let state = &Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); #[allow(clippy::expect_used)] let db = MockDb::new(&redis_interface::RedisSettings::default()) .await @@ -934,12 +1042,15 @@ mod merchant_connector_account_cache_tests { let connector_label = "stripe_USA"; let merchant_connector_id = "simple_merchant_connector_id"; let profile_id = "pro_max_ultra"; - + let key_manager_state = &state.into(); db.insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + Identifier::Merchant(merchant_id.to_string()), master_key, ) .await @@ -952,7 +1063,11 @@ mod merchant_connector_account_cache_tests { .unwrap(); let merchant_key = db - .get_merchant_key_store_by_merchant_id(merchant_id, &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &master_key.to_vec().into(), + ) .await .unwrap(); @@ -961,7 +1076,9 @@ mod merchant_connector_account_cache_tests { merchant_id: merchant_id.to_string(), connector_name: "stripe".to_string(), connector_account_details: domain::types::encrypt( + key_manager_state, serde_json::Value::default().into(), + Identifier::Merchant(merchant_key.merchant_id.clone()), merchant_key.key.get_inner().peek(), ) .await @@ -986,7 +1103,9 @@ mod merchant_connector_account_cache_tests { status: common_enums::ConnectorStatus::Inactive, connector_wallets_details: Some( domain::types::encrypt( + key_manager_state, serde_json::Value::default().into(), + Identifier::Merchant(merchant_key.merchant_id.clone()), merchant_key.key.get_inner().peek(), ) .await @@ -995,13 +1114,14 @@ mod merchant_connector_account_cache_tests { additional_merchant_data: None, }; - db.insert_merchant_connector_account(mca.clone(), &merchant_key) + db.insert_merchant_connector_account(key_manager_state, mca.clone(), &merchant_key) .await .unwrap(); let find_call = || async { Conversion::convert( db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, profile_id, &mca.connector_name, &merchant_key, diff --git a/crates/router/src/db/merchant_key_store.rs b/crates/router/src/db/merchant_key_store.rs index 42a862e2d3db..1cc12796bc6f 100644 --- a/crates/router/src/db/merchant_key_store.rs +++ b/crates/router/src/db/merchant_key_store.rs @@ -1,3 +1,4 @@ +use common_utils::types::keymanager::KeyManagerState; use error_stack::{report, ResultExt}; use masking::Secret; use router_env::{instrument, tracing}; @@ -19,12 +20,14 @@ use crate::{ pub trait MerchantKeyStoreInterface { async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult; async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult; @@ -37,12 +40,14 @@ pub trait MerchantKeyStoreInterface { #[cfg(feature = "olap")] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError>; async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError>; } @@ -52,10 +57,12 @@ impl MerchantKeyStoreInterface for Store { #[instrument(skip_all)] async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; + let merchant_id = merchant_key_store.merchant_id.clone(); merchant_key_store .construct_new() .await @@ -63,7 +70,7 @@ impl MerchantKeyStoreInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) } @@ -71,6 +78,7 @@ impl MerchantKeyStoreInterface for Store { #[instrument(skip_all)] async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult { @@ -89,7 +97,7 @@ impl MerchantKeyStoreInterface for Store { { fetch_func() .await? - .convert(key) + .convert(state, key, merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } @@ -104,7 +112,7 @@ impl MerchantKeyStoreInterface for Store { &ACCOUNTS_CACHE, ) .await? - .convert(key) + .convert(state, key, merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } @@ -146,6 +154,7 @@ impl MerchantKeyStoreInterface for Store { #[instrument(skip_all)] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError> { @@ -161,8 +170,9 @@ impl MerchantKeyStoreInterface for Store { }; futures::future::try_join_all(fetch_func().await?.into_iter().map(|key_store| async { + let merchant_id = key_store.merchant_id.clone(); key_store - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -171,6 +181,7 @@ impl MerchantKeyStoreInterface for Store { async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -181,8 +192,9 @@ impl MerchantKeyStoreInterface for Store { }; futures::future::try_join_all(fetch_func().await?.into_iter().map(|key_store| async { + let merchant_id = key_store.merchant_id.clone(); key_store - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -194,6 +206,7 @@ impl MerchantKeyStoreInterface for Store { impl MerchantKeyStoreInterface for MockDb { async fn insert_merchant_key_store( &self, + state: &KeyManagerState, merchant_key_store: domain::MerchantKeyStore, key: &Secret>, ) -> CustomResult { @@ -213,15 +226,16 @@ impl MerchantKeyStoreInterface for MockDb { .await .change_context(errors::StorageError::MockDbError)?; locked_merchant_key_store.push(merchant_key.clone()); - + let merchant_id = merchant_key.merchant_id.clone(); merchant_key - .convert(key) + .convert(state, key, merchant_id) .await .change_context(errors::StorageError::DecryptionError) } async fn get_merchant_key_store_by_merchant_id( &self, + state: &KeyManagerState, merchant_id: &str, key: &Secret>, ) -> CustomResult { @@ -234,7 +248,7 @@ impl MerchantKeyStoreInterface for MockDb { .ok_or(errors::StorageError::ValueNotFound(String::from( "merchant_key_store", )))? - .convert(key) + .convert(state, key, merchant_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } @@ -258,6 +272,7 @@ impl MerchantKeyStoreInterface for MockDb { #[cfg(feature = "olap")] async fn list_multiple_key_stores( &self, + state: &KeyManagerState, merchant_ids: Vec, key: &Secret>, ) -> CustomResult, errors::StorageError> { @@ -269,7 +284,7 @@ impl MerchantKeyStoreInterface for MockDb { .map(|merchant_key| async { merchant_key .to_owned() - .convert(key) + .convert(state, key, merchant_key.merchant_id.clone()) .await .change_context(errors::StorageError::DecryptionError) }), @@ -278,6 +293,7 @@ impl MerchantKeyStoreInterface for MockDb { } async fn get_all_key_stores( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let merchant_key_stores = self.merchant_key_store.lock().await; @@ -285,7 +301,7 @@ impl MerchantKeyStoreInterface for MockDb { futures::future::try_join_all(merchant_key_stores.iter().map(|merchant_key| async { merchant_key .to_owned() - .convert(key) + .convert(state, key, merchant_key.merchant_id.clone()) .await .change_context(errors::StorageError::DecryptionError) })) @@ -295,30 +311,54 @@ impl MerchantKeyStoreInterface for MockDb { #[cfg(test)] mod tests { + use std::sync::Arc; + + use common_utils::types::keymanager::Identifier; use time::macros::datetime; + use tokio::sync::oneshot; use crate::{ db::{merchant_key_store::MerchantKeyStoreInterface, MasterKeyInterface, MockDb}, + routes::{ + self, + app::{settings::Settings, StorageImpl}, + }, services, - types::domain::{self}, + types::domain, }; - #[allow(clippy::unwrap_used)] + #[allow(clippy::unwrap_used, clippy::expect_used)] #[tokio::test] async fn test_mock_db_merchant_key_store_interface() { + let conf = Settings::new().expect("invalid settings"); + let tx: oneshot::Sender<()> = oneshot::channel().0; + let app_state = Box::pin(routes::AppState::with_storage( + conf, + StorageImpl::PostgresqlTest, + tx, + Box::new(services::MockApiClient), + )) + .await; + let state = &Arc::new(app_state) + .get_session_state("public", || {}) + .unwrap(); #[allow(clippy::expect_used)] let mock_db = MockDb::new(&redis_interface::RedisSettings::default()) .await .expect("Failed to create mock DB"); let master_key = mock_db.get_master_key(); let merchant_id = "merchant1"; - + let identifier = Identifier::Merchant(merchant_id.to_string()); + let key_manager_state = &state.into(); let merchant_key1 = mock_db .insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + identifier.clone(), master_key, ) .await @@ -331,7 +371,11 @@ mod tests { .unwrap(); let found_merchant_key1 = mock_db - .get_merchant_key_store_by_merchant_id(merchant_id, &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &master_key.to_vec().into(), + ) .await .unwrap(); @@ -340,10 +384,13 @@ mod tests { let insert_duplicate_merchant_key1_result = mock_db .insert_merchant_key_store( + key_manager_state, domain::MerchantKeyStore { merchant_id: merchant_id.into(), key: domain::types::encrypt( + key_manager_state, services::generate_aes256_key().unwrap().to_vec().into(), + identifier.clone(), master_key, ) .await @@ -356,12 +403,20 @@ mod tests { assert!(insert_duplicate_merchant_key1_result.is_err()); let find_non_existent_merchant_key_result = mock_db - .get_merchant_key_store_by_merchant_id("non_existent", &master_key.to_vec().into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + "non_existent", + &master_key.to_vec().into(), + ) .await; assert!(find_non_existent_merchant_key_result.is_err()); let find_merchant_key_with_incorrect_master_key_result = mock_db - .get_merchant_key_store_by_merchant_id(merchant_id, &vec![0; 32].into()) + .get_merchant_key_store_by_merchant_id( + key_manager_state, + merchant_id, + &vec![0; 32].into(), + ) .await; assert!(find_merchant_key_with_incorrect_master_key_result.is_err()); } diff --git a/crates/router/src/db/user/sample_data.rs b/crates/router/src/db/user/sample_data.rs index 5a23666c7b10..cabf62b850e2 100644 --- a/crates/router/src/db/user/sample_data.rs +++ b/crates/router/src/db/user/sample_data.rs @@ -1,3 +1,4 @@ +use common_utils::types::keymanager::KeyManagerState; use diesel_models::{ errors::DatabaseError, query::user::sample_data as sample_data_queries, @@ -20,6 +21,7 @@ use crate::{connection::pg_connection_write, core::errors::CustomResult, service pub trait BatchSampleDataInterface { async fn insert_payment_intents_batch_for_sample_data( &self, + state: &KeyManagerState, batch: Vec, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError>; @@ -36,6 +38,7 @@ pub trait BatchSampleDataInterface { async fn delete_payment_intents_for_sample_data( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError>; @@ -55,6 +58,7 @@ pub trait BatchSampleDataInterface { impl BatchSampleDataInterface for Store { async fn insert_payment_intents_batch_for_sample_data( &self, + state: &KeyManagerState, batch: Vec, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { @@ -68,13 +72,17 @@ impl BatchSampleDataInterface for Store { .change_context(StorageError::EncryptionError) })) .await?; - sample_data_queries::insert_payment_intents(&conn, new_intents) .await .map_err(diesel_error_to_data_error) .map(|v| { try_join_all(v.into_iter().map(|payment_intent| { - PaymentIntent::convert_back(payment_intent, key_store.key.get_inner()) + PaymentIntent::convert_back( + state, + payment_intent, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) })? @@ -111,6 +119,7 @@ impl BatchSampleDataInterface for Store { async fn delete_payment_intents_for_sample_data( &self, + state: &KeyManagerState, merchant_id: &str, key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { @@ -122,7 +131,12 @@ impl BatchSampleDataInterface for Store { .map_err(diesel_error_to_data_error) .map(|v| { try_join_all(v.into_iter().map(|payment_intent| { - PaymentIntent::convert_back(payment_intent, key_store.key.get_inner()) + PaymentIntent::convert_back( + state, + payment_intent, + key_store.key.get_inner(), + key_store.merchant_id.clone(), + ) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) })? @@ -162,6 +176,7 @@ impl BatchSampleDataInterface for Store { impl BatchSampleDataInterface for storage_impl::MockDb { async fn insert_payment_intents_batch_for_sample_data( &self, + _state: &KeyManagerState, _batch: Vec, _key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { @@ -184,6 +199,7 @@ impl BatchSampleDataInterface for storage_impl::MockDb { async fn delete_payment_intents_for_sample_data( &self, + _state: &KeyManagerState, _merchant_id: &str, _key_store: &MerchantKeyStore, ) -> CustomResult, StorageError> { diff --git a/crates/router/src/db/user_key_store.rs b/crates/router/src/db/user_key_store.rs index c7e48037e96f..e7dbe531c65e 100644 --- a/crates/router/src/db/user_key_store.rs +++ b/crates/router/src/db/user_key_store.rs @@ -1,4 +1,4 @@ -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState}; use error_stack::{report, ResultExt}; use masking::Secret; use router_env::{instrument, tracing}; @@ -18,18 +18,21 @@ use crate::{ pub trait UserKeyStoreInterface { async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult; async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult; async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError>; } @@ -39,10 +42,12 @@ impl UserKeyStoreInterface for Store { #[instrument(skip_all)] async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult { let conn = connection::pg_connection_write(self).await?; + let user_id = user_key_store.user_id.clone(); user_key_store .construct_new() .await @@ -50,7 +55,7 @@ impl UserKeyStoreInterface for Store { .insert(&conn) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) } @@ -58,6 +63,7 @@ impl UserKeyStoreInterface for Store { #[instrument(skip_all)] async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult { @@ -66,13 +72,14 @@ impl UserKeyStoreInterface for Store { diesel_models::user_key_store::UserKeyStore::find_by_user_id(&conn, user_id) .await .map_err(|error| report!(errors::StorageError::from(error)))? - .convert(key) + .convert(state, key, user_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let conn = connection::pg_connection_read(self).await?; @@ -84,8 +91,9 @@ impl UserKeyStoreInterface for Store { }; futures::future::try_join_all(fetch_func().await?.into_iter().map(|key_store| async { + let user_id = key_store.user_id.clone(); key_store - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -98,6 +106,7 @@ impl UserKeyStoreInterface for MockDb { #[instrument(skip_all)] async fn insert_user_key_store( &self, + state: &KeyManagerState, user_key_store: domain::UserKeyStore, key: &Secret>, ) -> CustomResult { @@ -117,23 +126,25 @@ impl UserKeyStoreInterface for MockDb { .await .change_context(errors::StorageError::MockDbError)?; locked_user_key_store.push(user_key_store.clone()); - + let user_id = user_key_store.user_id.clone(); user_key_store - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) } async fn get_all_user_key_store( &self, + state: &KeyManagerState, key: &Secret>, ) -> CustomResult, errors::StorageError> { let user_key_store = self.user_key_store.lock().await; futures::future::try_join_all(user_key_store.iter().map(|user_key| async { + let user_id = user_key.user_id.clone(); user_key .to_owned() - .convert(key) + .convert(state, key, user_id) .await .change_context(errors::StorageError::DecryptionError) })) @@ -143,6 +154,7 @@ impl UserKeyStoreInterface for MockDb { #[instrument(skip_all)] async fn get_user_key_store_by_user_id( &self, + state: &KeyManagerState, user_id: &str, key: &Secret>, ) -> CustomResult { @@ -156,7 +168,7 @@ impl UserKeyStoreInterface for MockDb { "No user_key_store is found for user_id={}", user_id )))? - .convert(key) + .convert(state, key, user_id.to_string()) .await .change_context(errors::StorageError::DecryptionError) } diff --git a/crates/router/src/lib.rs b/crates/router/src/lib.rs index 38000f7b6648..6405a3b84791 100644 --- a/crates/router/src/lib.rs +++ b/crates/router/src/lib.rs @@ -193,7 +193,11 @@ pub async fn start_server(conf: settings::Settings) -> Applicatio let api_client = Box::new( services::ProxyClient::new( conf.proxy.clone(), - services::proxy_bypass_urls(&conf.locker, &conf.proxy.bypass_proxy_urls), + services::proxy_bypass_urls( + conf.key_manager.get_inner(), + &conf.locker, + &conf.proxy.bypass_proxy_urls, + ), ) .map_err(|error| { errors::ApplicationError::ApiClientError(error.current_context().clone()) diff --git a/crates/router/src/routes/app.rs b/crates/router/src/routes/app.rs index 0d7d042f7af3..b733e76289e0 100644 --- a/crates/router/src/routes/app.rs +++ b/crates/router/src/routes/app.rs @@ -110,6 +110,7 @@ pub trait SessionStateInfo { fn event_handler(&self) -> EventsHandler; fn get_request_id(&self) -> Option; fn add_request_id(&mut self, request_id: RequestId); + fn session_state(&self) -> SessionState; } impl SessionStateInfo for SessionState { @@ -130,6 +131,9 @@ impl SessionStateInfo for SessionState { self.store.add_request_id(request_id.to_string()); self.request_id.replace(request_id); } + fn session_state(&self) -> SessionState { + self.clone() + } } #[derive(Clone)] pub struct AppState { diff --git a/crates/router/src/routes/payment_methods.rs b/crates/router/src/routes/payment_methods.rs index 003cd71c00d5..25df0ef64345 100644 --- a/crates/router/src/routes/payment_methods.rs +++ b/crates/router/src/routes/payment_methods.rs @@ -40,7 +40,7 @@ pub async fn create_payment_method_api( json_payload.into_inner(), |state, auth, req, _| async move { Box::pin(cards::get_client_secret_or_add_payment_method( - state, + &state, req, &auth.merchant_account, &auth.key_store, @@ -88,9 +88,11 @@ async fn get_merchant_account( state: &SessionState, merchant_id: &str, ) -> CustomResult<(MerchantKeyStore, domain::MerchantAccount), errors::ApiErrorResponse> { + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -99,7 +101,7 @@ async fn get_merchant_account( let merchant_account = state .store - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; Ok((key_store, merchant_account)) @@ -530,7 +532,7 @@ pub async fn default_payment_method_set_api( payload, |state, auth: auth::AuthenticationData, default_payment_method, _| async move { cards::set_default_payment_method( - &*state.clone().store, + &state, auth.merchant_account.merchant_id, auth.key_store, customer_id, diff --git a/crates/router/src/routes/recon.rs b/crates/router/src/routes/recon.rs index aabbd637ae7d..d8100e8d3414 100644 --- a/crates/router/src/routes/recon.rs +++ b/crates/router/src/routes/recon.rs @@ -85,15 +85,17 @@ pub async fn send_recon_request( .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)? .merchant_id; + let key_manager_state = &(&state).into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_str(), &db.get_master_key().to_vec().into(), ) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id.as_str(), &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -130,7 +132,12 @@ pub async fn send_recon_request( }; let response = db - .update_merchant(merchant_account, updated_merchant_account, &key_store) + .update_merchant( + key_manager_state, + merchant_account, + updated_merchant_account, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { @@ -162,6 +169,7 @@ pub async fn recon_merchant_account_update( let key_store = db .get_merchant_key_store_by_merchant_id( + &(&state).into(), &req.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -169,7 +177,7 @@ pub async fn recon_merchant_account_update( .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; let merchant_account = db - .find_merchant_account_by_merchant_id(merchant_id, &key_store) + .find_merchant_account_by_merchant_id(&(&state).into(), merchant_id, &key_store) .await .to_not_found_response(errors::ApiErrorResponse::MerchantAccountNotFound)?; @@ -178,7 +186,12 @@ pub async fn recon_merchant_account_update( }; let response = db - .update_merchant(merchant_account, updated_merchant_account, &key_store) + .update_merchant( + &(&state).into(), + merchant_account, + updated_merchant_account, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable_lazy(|| { diff --git a/crates/router/src/services/api.rs b/crates/router/src/services/api.rs index 5dc46d356525..ea9ce1774394 100644 --- a/crates/router/src/services/api.rs +++ b/crates/router/src/services/api.rs @@ -474,12 +474,19 @@ pub async fn send_request( let should_bypass_proxy = url .as_str() .starts_with(&state.conf.connectors.dummyconnector.base_url) - || proxy_bypass_urls(&state.conf.locker, &state.conf.proxy.bypass_proxy_urls) - .contains(&url.to_string()); + || proxy_bypass_urls( + state.conf.key_manager.get_inner(), + &state.conf.locker, + &state.conf.proxy.bypass_proxy_urls, + ) + .contains(&url.to_string()); #[cfg(not(feature = "dummy_connector"))] - let should_bypass_proxy = - proxy_bypass_urls(&state.conf.locker, &state.conf.proxy.bypass_proxy_urls) - .contains(&url.to_string()); + let should_bypass_proxy = proxy_bypass_urls( + &state.conf.key_manager.get_inner(), + &state.conf.locker, + &state.conf.proxy.bypass_proxy_urls, + ) + .contains(&url.to_string()); let client = client::create_client( &state.conf.proxy, should_bypass_proxy, diff --git a/crates/router/src/services/api/client.rs b/crates/router/src/services/api/client.rs index f3cb2f3f314e..4dda8eab6271 100644 --- a/crates/router/src/services/api/client.rs +++ b/crates/router/src/services/api/client.rs @@ -15,7 +15,7 @@ use crate::{ errors::{ApiClientError, CustomResult}, payments, }, - routes::SessionState, + routes::{app::settings::KeyManagerConfig, SessionState}, }; static NON_PROXIED_CLIENT: OnceCell = OnceCell::new(); @@ -110,7 +110,12 @@ pub fn create_client( } } -pub fn proxy_bypass_urls(locker: &Locker, config_whitelist: &[String]) -> Vec { +pub fn proxy_bypass_urls( + key_manager: &KeyManagerConfig, + locker: &Locker, + config_whitelist: &[String], +) -> Vec { + let key_manager_host = key_manager.url.to_owned(); let locker_host = locker.host.to_owned(); let locker_host_rs = locker.host_rs.to_owned(); @@ -126,8 +131,11 @@ pub fn proxy_bypass_urls(locker: &Locker, config_whitelist: &[String]) -> Vec RouterResult<(AuthenticationData, AuthenticationType)> { + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, self.0.as_ref(), &state.store().get_master_key().to_vec().into(), ) @@ -552,7 +561,7 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(self.0.as_ref(), &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, self.0.as_ref(), &key_store) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -590,10 +599,10 @@ where ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let publishable_key = get_api_key(request_headers).change_context(errors::ApiErrorResponse::Unauthorized)?; - + let key_manager_state = &(&state.session_state()).into(); state .store() - .find_merchant_account_by_publishable_key(publishable_key) + .find_merchant_account_by_publishable_key(key_manager_state, publishable_key) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -818,10 +827,11 @@ where let permissions = authorization::get_permissions(state, &payload).await?; authorization::check_authorization(&self.0, &permissions)?; - + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, &payload.merchant_id, &state.store().get_master_key().to_vec().into(), ) @@ -831,7 +841,11 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(&payload.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InvalidJwtToken)?; @@ -868,10 +882,11 @@ where let permissions = authorization::get_permissions(state, &payload).await?; authorization::check_authorization(&self.0, &permissions)?; - + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, &payload.merchant_id, &state.store().get_master_key().to_vec().into(), ) @@ -881,7 +896,11 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(&payload.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) .await .change_context(errors::ApiErrorResponse::InvalidJwtToken)?; @@ -963,10 +982,11 @@ where state: &A, ) -> RouterResult<(AuthenticationData, AuthenticationType)> { let payload = parse_jwt_payload::(request_headers, state).await?; - + let key_manager_state = &(&state.session_state()).into(); let key_store = state .store() .get_merchant_key_store_by_merchant_id( + key_manager_state, &payload.merchant_id, &state.store().get_master_key().to_vec().into(), ) @@ -976,7 +996,11 @@ where let merchant = state .store() - .find_merchant_account_by_merchant_id(&payload.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &payload.merchant_id, + &key_store, + ) .await .to_not_found_response(errors::ApiErrorResponse::Unauthorized)?; diff --git a/crates/router/src/types/api/admin.rs b/crates/router/src/types/api/admin.rs index 884e47c6fc4a..054888d1dfe1 100644 --- a/crates/router/src/types/api/admin.rs +++ b/crates/router/src/types/api/admin.rs @@ -8,13 +8,17 @@ pub use api_models::admin::{ MerchantId, PaymentMethodsEnabled, ToggleAllKVRequest, ToggleAllKVResponse, ToggleKVRequest, ToggleKVResponse, WebhookDetails, }; -use common_utils::ext_traits::{AsyncExt, Encode, ValueExt}; +use common_utils::{ + ext_traits::{AsyncExt, Encode, ValueExt}, + types::keymanager::Identifier, +}; use error_stack::{report, ResultExt}; use hyperswitch_domain_models::{merchant_key_store::MerchantKeyStore, type_encryption::decrypt}; use masking::{ExposeInterface, PeekInterface, Secret}; use crate::{ core::{errors, payment_methods::cards::create_encrypted_data}, + routes::SessionState, types::{domain, storage, transformers::ForeignTryFrom}, }; @@ -84,11 +88,14 @@ impl ForeignTryFrom for MerchantAccountResponse { } pub async fn business_profile_response( + state: &SessionState, item: storage::business_profile::BusinessProfile, key_store: &MerchantKeyStore, ) -> Result> { let outgoing_webhook_custom_http_headers = decrypt::( + &state.into(), item.outgoing_webhook_custom_http_headers.clone(), + Identifier::Merchant(key_store.merchant_id.clone()), key_store.key.get_inner().peek(), ) .await @@ -147,6 +154,7 @@ pub async fn business_profile_response( } pub async fn create_business_profile( + state: &SessionState, merchant_account: domain::MerchantAccount, request: BusinessProfileCreate, key_store: &MerchantKeyStore, @@ -188,7 +196,7 @@ pub async fn create_business_profile( .transpose()?; let outgoing_webhook_custom_http_headers = request .outgoing_webhook_custom_http_headers - .async_map(|headers| create_encrypted_data(key_store, headers)) + .async_map(|headers| create_encrypted_data(state, key_store, headers)) .await .transpose() .change_context(errors::ApiErrorResponse::InternalServerError) diff --git a/crates/router/src/types/api/mandates.rs b/crates/router/src/types/api/mandates.rs index 71b90ab41c3d..686f7f3ce495 100644 --- a/crates/router/src/types/api/mandates.rs +++ b/crates/router/src/types/api/mandates.rs @@ -2,7 +2,6 @@ use api_models::mandates; pub use api_models::mandates::{MandateId, MandateResponse, MandateRevokedResponse}; use common_utils::ext_traits::OptionExt; use error_stack::ResultExt; -use masking::PeekInterface; use serde::{Deserialize, Serialize}; use crate::{ @@ -73,8 +72,8 @@ impl MandateResponseExt for MandateResponse { } else { payment_methods::cards::get_card_details_without_locker_fallback( &payment_method, - key_store.key.get_inner().peek(), state, + &key_store, ) .await? }; diff --git a/crates/router/src/types/domain/address.rs b/crates/router/src/types/domain/address.rs index 0ed09a35013e..e14ab8ea6410 100644 --- a/crates/router/src/types/domain/address.rs +++ b/crates/router/src/types/domain/address.rs @@ -1,18 +1,19 @@ use async_trait::async_trait; use common_utils::{ - crypto, date_time, + crypto::{self, Encryptable}, + date_time, + encryption::Encryption, errors::{CustomResult, ValidationError}, id_type, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, }; -use diesel_models::{address::AddressUpdateInternal, encryption::Encryption, enums}; +use diesel_models::{address::AddressUpdateInternal, enums}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; +use rustc_hash::FxHashMap; use time::{OffsetDateTime, PrimitiveDateTime}; -use super::{ - behaviour, - types::{self, AsyncLift}, -}; +use super::{behaviour, types}; #[derive(Clone, Debug, serde::Serialize)] pub struct Address { @@ -73,8 +74,10 @@ impl behaviour::Conversion for CustomerAddress { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult { let customer_id = other @@ -84,7 +87,7 @@ impl behaviour::Conversion for CustomerAddress { field_name: "customer_id".to_string(), })?; - let address = Address::convert_back(other, key).await?; + let address = Address::convert_back(state, other, key, key_store_ref_id).await?; Ok(Self { address, @@ -117,8 +120,10 @@ impl behaviour::Conversion for PaymentAddress { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult { let payment_id = other .payment_id @@ -129,7 +134,7 @@ impl behaviour::Conversion for PaymentAddress { let customer_id = other.customer_id.clone(); - let address = Address::convert_back(other, key).await?; + let address = Address::convert_back(state, other, key, key_store_ref_id).await?; Ok(Self { address, @@ -180,36 +185,45 @@ impl behaviour::Conversion for Address { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult { - async { - let inner_decrypt = |inner| types::decrypt(inner, key.peek()); - let inner_decrypt_email = |inner| types::decrypt(inner, key.peek()); - Ok::>(Self { - id: other.id, - address_id: other.address_id, - city: other.city, - country: other.country, - line1: other.line1.async_lift(inner_decrypt).await?, - line2: other.line2.async_lift(inner_decrypt).await?, - line3: other.line3.async_lift(inner_decrypt).await?, - state: other.state.async_lift(inner_decrypt).await?, - zip: other.zip.async_lift(inner_decrypt).await?, - first_name: other.first_name.async_lift(inner_decrypt).await?, - last_name: other.last_name.async_lift(inner_decrypt).await?, - phone_number: other.phone_number.async_lift(inner_decrypt).await?, - country_code: other.country_code, - created_at: other.created_at, - modified_at: other.modified_at, - updated_by: other.updated_by, - merchant_id: other.merchant_id, - email: other.email.async_lift(inner_decrypt_email).await?, - }) - } + let identifier = Identifier::Merchant(other.merchant_id.clone()); + let decrypted: FxHashMap>> = types::batch_decrypt( + state, + diesel_models::Address::to_encryptable(other.clone()), + identifier.clone(), + key.peek(), + ) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting".to_string(), + })?; + let encryptable_address = diesel_models::Address::from_encryptable(decrypted) + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting".to_string(), + })?; + Ok(Self { + id: other.id, + address_id: other.address_id, + city: other.city, + country: other.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + state: encryptable_address.state, + zip: encryptable_address.zip, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: other.country_code, + created_at: other.created_at, + modified_at: other.modified_at, + updated_by: other.updated_by, + merchant_id: other.merchant_id, + email: encryptable_address.email, }) } diff --git a/crates/router/src/types/domain/customer.rs b/crates/router/src/types/domain/customer.rs index c5b0b3701cb8..b7ed05e3506c 100644 --- a/crates/router/src/types/domain/customer.rs +++ b/crates/router/src/types/domain/customer.rs @@ -1,10 +1,16 @@ -use common_utils::{crypto, date_time, id_type, pii}; -use diesel_models::{customers::CustomerUpdateInternal, encryption::Encryption}; +use api_models::customers::CustomerRequestWithEncryption; +use common_utils::{ + crypto, date_time, + encryption::Encryption, + id_type, pii, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, +}; +use diesel_models::customers::CustomerUpdateInternal; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use time::PrimitiveDateTime; -use super::types::{self, AsyncLift}; +use super::types; use crate::errors::{CustomResult, ValidationError}; #[derive(Clone, Debug)] @@ -53,36 +59,49 @@ impl super::behaviour::Conversion for Customer { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult where Self: Sized, { - async { - let inner_decrypt = |inner| types::decrypt(inner, key.peek()); - let inner_decrypt_email = |inner| types::decrypt(inner, key.peek()); - Ok::>(Self { - id: Some(item.id), - customer_id: item.customer_id, - merchant_id: item.merchant_id, - name: item.name.async_lift(inner_decrypt).await?, - email: item.email.async_lift(inner_decrypt_email).await?, - phone: item.phone.async_lift(inner_decrypt).await?, - phone_country_code: item.phone_country_code, - description: item.description, - created_at: item.created_at, - metadata: item.metadata, - modified_at: item.modified_at, - connector_customer: item.connector_customer, - address_id: item.address_id, - default_payment_method_id: item.default_payment_method_id, - updated_by: item.updated_by, - }) - } + let decrypted = types::batch_decrypt( + state, + CustomerRequestWithEncryption::to_encryptable(CustomerRequestWithEncryption { + name: item.name.clone(), + phone: item.phone.clone(), + email: item.email.clone(), + }), + Identifier::Merchant(item.merchant_id.clone()), + key.peek(), + ) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting customer data".to_string(), + })?; + let encryptable_customer = CustomerRequestWithEncryption::from_encryptable(decrypted) + .change_context(ValidationError::InvalidValue { + message: "Failed while decrypting customer data".to_string(), + })?; + + Ok(Self { + id: Some(item.id), + customer_id: item.customer_id, + merchant_id: item.merchant_id, + name: encryptable_customer.name, + email: encryptable_customer.email, + phone: encryptable_customer.phone, + phone_country_code: item.phone_country_code, + description: item.description, + created_at: item.created_at, + metadata: item.metadata, + modified_at: item.modified_at, + connector_customer: item.connector_customer, + address_id: item.address_id, + default_payment_method_id: item.default_payment_method_id, + updated_by: item.updated_by, }) } diff --git a/crates/router/src/types/domain/event.rs b/crates/router/src/types/domain/event.rs index 74a6cfb47b54..c5eca1ab5eab 100644 --- a/crates/router/src/types/domain/event.rs +++ b/crates/router/src/types/domain/event.rs @@ -1,14 +1,18 @@ -use common_utils::crypto::OptionalEncryptableSecretString; +use common_utils::{ + crypto::OptionalEncryptableSecretString, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, +}; use diesel_models::{ enums::{EventClass, EventObjectType, EventType, WebhookDeliveryAttempt}, events::{EventMetadata, EventUpdateInternal}, + EventWithEncryption, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; use crate::{ errors::{CustomResult, ValidationError}, - types::domain::types::{self, AsyncLift}, + types::domain::types, }; #[derive(Clone, Debug)] @@ -80,41 +84,49 @@ impl super::behaviour::Conversion for Event { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + key_store_ref_id: String, ) -> CustomResult where Self: Sized, { - async { - Ok::>(Self { - event_id: item.event_id, - event_type: item.event_type, - event_class: item.event_class, - is_webhook_notified: item.is_webhook_notified, - primary_object_id: item.primary_object_id, - primary_object_type: item.primary_object_type, - created_at: item.created_at, - merchant_id: item.merchant_id, - business_profile_id: item.business_profile_id, - primary_object_created_at: item.primary_object_created_at, - idempotent_event_id: item.idempotent_event_id, - initial_attempt_id: item.initial_attempt_id, - request: item - .request - .async_lift(|inner| types::decrypt(inner, key.peek())) - .await?, - response: item - .response - .async_lift(|inner| types::decrypt(inner, key.peek())) - .await?, - delivery_attempt: item.delivery_attempt, - metadata: item.metadata, - }) - } + let decrypted = types::batch_decrypt( + state, + EventWithEncryption::to_encryptable(EventWithEncryption { + request: item.request.clone(), + response: item.response.clone(), + }), + Identifier::Merchant(key_store_ref_id.clone()), + key.peek(), + ) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting event data".to_string(), + })?; + let encryptable_event = EventWithEncryption::from_encryptable(decrypted).change_context( + ValidationError::InvalidValue { + message: "Failed while decrypting event data".to_string(), + }, + )?; + Ok(Self { + event_id: item.event_id, + event_type: item.event_type, + event_class: item.event_class, + is_webhook_notified: item.is_webhook_notified, + primary_object_id: item.primary_object_id, + primary_object_type: item.primary_object_type, + created_at: item.created_at, + merchant_id: item.merchant_id, + business_profile_id: item.business_profile_id, + primary_object_created_at: item.primary_object_created_at, + idempotent_event_id: item.idempotent_event_id, + initial_attempt_id: item.initial_attempt_id, + request: encryptable_event.request, + response: encryptable_event.response, + delivery_attempt: item.delivery_attempt, + metadata: item.metadata, }) } diff --git a/crates/router/src/types/domain/merchant_connector_account.rs b/crates/router/src/types/domain/merchant_connector_account.rs index 2cf091eda12e..6f654848f606 100644 --- a/crates/router/src/types/domain/merchant_connector_account.rs +++ b/crates/router/src/types/domain/merchant_connector_account.rs @@ -1,13 +1,12 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, date_time, + encryption::Encryption, errors::{CustomResult, ValidationError}, pii, + types::keymanager::{Identifier, KeyManagerState}, }; -use diesel_models::{ - encryption::Encryption, enums, - merchant_connector_account::MerchantConnectorAccountUpdateInternal, -}; +use diesel_models::{enums, merchant_connector_account::MerchantConnectorAccountUpdateInternal}; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -108,15 +107,20 @@ impl behaviour::Conversion for MerchantConnectorAccount { } async fn convert_back( + state: &KeyManagerState, other: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult { + let identifier = Identifier::Merchant(other.merchant_id.clone()); Ok(Self { id: Some(other.id), merchant_id: other.merchant_id, connector_name: other.connector_name, - connector_account_details: Encryptable::decrypt( + connector_account_details: Encryptable::decrypt_via_api( + state, other.connector_account_details, + identifier.clone(), key.peek(), GcmAes256, ) @@ -145,7 +149,7 @@ impl behaviour::Conversion for MerchantConnectorAccount { status: other.status, connector_wallets_details: other .connector_wallets_details - .async_lift(|inner| types::decrypt(inner, key.peek())) + .async_lift(|inner| types::decrypt(state, inner, identifier.clone(), key.peek())) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting connector wallets details".to_string(), diff --git a/crates/router/src/types/domain/types.rs b/crates/router/src/types/domain/types.rs index 151ae61f5fe5..53239cd27cf9 100644 --- a/crates/router/src/types/domain/types.rs +++ b/crates/router/src/types/domain/types.rs @@ -1,6 +1,7 @@ use common_utils::types::keymanager::KeyManagerState; pub use hyperswitch_domain_models::type_encryption::{ - decrypt, encrypt, encrypt_optional, AsyncLift, Lift, TypeEncryption, + batch_decrypt, batch_encrypt, decrypt, encrypt, encrypt_optional, AsyncLift, Lift, + TypeEncryption, }; impl From<&crate::SessionState> for KeyManagerState { diff --git a/crates/router/src/types/domain/user.rs b/crates/router/src/types/domain/user.rs index c4c67796d466..ec44103a8f06 100644 --- a/crates/router/src/types/domain/user.rs +++ b/crates/router/src/types/domain/user.rs @@ -7,8 +7,11 @@ use common_enums::TokenPurpose; #[cfg(not(feature = "v2"))] use common_utils::id_type; #[cfg(feature = "keymanager_create")] -use common_utils::types::keymanager::{EncryptionCreateRequest, Identifier}; -use common_utils::{crypto::Encryptable, errors::CustomResult, new_type::MerchantName, pii}; +use common_utils::types::keymanager::EncryptionCreateRequest; +use common_utils::{ + crypto::Encryptable, errors::CustomResult, new_type::MerchantName, pii, + types::keymanager::Identifier, +}; use diesel_models::{ enums::{TotpStatus, UserStatus}, organization as diesel_org, @@ -368,6 +371,7 @@ impl NewUserMerchant { if state .store .get_merchant_key_store_by_merchant_id( + &(&state).into(), self.get_merchant_id().as_str(), &state.store.get_master_key().to_vec().into(), ) @@ -949,9 +953,14 @@ impl UserFromStorage { pub async fn get_or_create_key_store(&self, state: &SessionState) -> UserResult { let master_key = state.store.get_master_key(); + let key_manager_state = &state.into(); let key_store_result = state .global_store - .get_user_key_store_by_user_id(self.get_user_id(), &master_key.to_vec().into()) + .get_user_key_store_by_user_id( + key_manager_state, + self.get_user_id(), + &master_key.to_vec().into(), + ) .await; if let Ok(key_store) = key_store_result { @@ -968,16 +977,21 @@ impl UserFromStorage { let key_store = UserKeyStore { user_id: self.get_user_id().to_string(), - key: domain_types::encrypt(key.to_vec().into(), master_key) - .await - .change_context(UserErrors::InternalServerError)?, + key: domain_types::encrypt( + key_manager_state, + key.to_vec().into(), + Identifier::User(self.get_user_id().to_string()), + master_key, + ) + .await + .change_context(UserErrors::InternalServerError)?, created_at: common_utils::date_time::now(), }; #[cfg(feature = "keymanager_create")] { common_utils::keymanager::create_key_in_key_manager( - &state.into(), + key_manager_state, EncryptionCreateRequest { identifier: Identifier::User(key_store.user_id.clone()), }, @@ -988,7 +1002,7 @@ impl UserFromStorage { state .global_store - .insert_user_key_store(key_store, &master_key.to_vec().into()) + .insert_user_key_store(key_manager_state, key_store, &master_key.to_vec().into()) .await .change_context(UserErrors::InternalServerError) } else { @@ -1014,10 +1028,11 @@ impl UserFromStorage { if self.0.totp_secret.is_none() { return Ok(None); } - + let key_manager_state = &state.into(); let user_key_store = state .global_store .get_user_key_store_by_user_id( + key_manager_state, self.get_user_id(), &state.store.get_master_key().to_vec().into(), ) @@ -1025,7 +1040,9 @@ impl UserFromStorage { .change_context(UserErrors::InternalServerError)?; Ok(domain_types::decrypt::( + key_manager_state, self.0.totp_secret.clone(), + Identifier::User(user_key_store.user_id.clone()), user_key_store.key.peek(), ) .await @@ -1141,6 +1158,7 @@ impl SignInWithMultipleRolesStrategy { let merchant_accounts = state .store .list_multiple_merchant_accounts( + &state.into(), self.user_roles .iter() .map(|role| role.merchant_id.clone()) diff --git a/crates/router/src/types/domain/user_key_store.rs b/crates/router/src/types/domain/user_key_store.rs index 4c1427d58dc6..3a1a9a60e95f 100644 --- a/crates/router/src/types/domain/user_key_store.rs +++ b/crates/router/src/types/domain/user_key_store.rs @@ -1,6 +1,7 @@ use common_utils::{ crypto::{Encryptable, GcmAes256}, date_time, + types::keymanager::{Identifier, KeyManagerState}, }; use error_stack::ResultExt; use masking::{PeekInterface, Secret}; @@ -32,14 +33,17 @@ impl super::behaviour::Conversion for UserKeyStore { } async fn convert_back( + state: &KeyManagerState, item: Self::DstType, key: &Secret>, + _key_store_ref_id: String, ) -> CustomResult where Self: Sized, { + let identifier = Identifier::User(item.user_id.clone()); Ok(Self { - key: Encryptable::decrypt(item.key, key.peek(), GcmAes256) + key: Encryptable::decrypt_via_api(state, item.key, identifier, key.peek(), GcmAes256) .await .change_context(ValidationError::InvalidValue { message: "Failed while decrypting customer data".to_string(), diff --git a/crates/router/src/utils.rs b/crates/router/src/utils.rs index 64e2f534b7ab..a36aee4593ea 100644 --- a/crates/router/src/utils.rs +++ b/crates/router/src/utils.rs @@ -14,19 +14,25 @@ pub mod verify_connector; use std::fmt::Debug; -use api_models::{enums, payments, webhooks}; +use api_models::{ + enums, + payments::{self, AddressDetailsWithPhone}, + webhooks, +}; use base64::Engine; -use common_utils::id_type; pub use common_utils::{ crypto, ext_traits::{ByteSliceExt, BytesExt, Encode, StringExt, ValueExt}, fp_utils::when, validation::validate_email, }; +use common_utils::{ + id_type, + types::keymanager::{Identifier, KeyManagerState, ToEncryptable}, +}; use error_stack::ResultExt; -use hyperswitch_domain_models::payments::PaymentIntent; +use hyperswitch_domain_models::{payments::PaymentIntent, type_encryption::batch_encrypt}; use image::Luma; -use masking::ExposeInterface; use nanoid::nanoid; use qrcode; use router_env::metrics::add_attributes; @@ -43,19 +49,10 @@ use crate::{ errors::{self, CustomResult, RouterResult, StorageErrorExt}, utils, webhooks as webhooks_core, }, - db::StorageInterface, logger, - routes::metrics, + routes::{metrics, SessionState}, services, - types::{ - self, - domain::{ - self, - types::{encrypt_optional, AsyncLift}, - }, - storage, - transformers::ForeignFrom, - }, + types::{self, domain, storage, transformers::ForeignFrom}, }; pub mod error_parser { @@ -194,14 +191,17 @@ impl QrImage { } pub async fn find_payment_intent_from_payment_id_type( - db: &dyn StorageInterface, + state: &SessionState, payment_id_type: payments::PaymentIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let key_manager_state: KeyManagerState = state.into(); + let db = &*state.store; match payment_id_type { payments::PaymentIdType::PaymentIntentId(payment_id) => db .find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, &payment_id, &merchant_account.merchant_id, key_store, @@ -219,6 +219,7 @@ pub async fn find_payment_intent_from_payment_id_type( .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; db.find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, &attempt.payment_id, &merchant_account.merchant_id, key_store, @@ -237,6 +238,7 @@ pub async fn find_payment_intent_from_payment_id_type( .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; db.find_payment_intent_by_payment_id_merchant_id( + &key_manager_state, &attempt.payment_id, &merchant_account.merchant_id, key_store, @@ -252,12 +254,13 @@ pub async fn find_payment_intent_from_payment_id_type( } pub async fn find_payment_intent_from_refund_id_type( - db: &dyn StorageInterface, + state: &SessionState, refund_id_type: webhooks::RefundIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, connector_name: &str, ) -> CustomResult { + let db = &*state.store; let refund = match refund_id_type { webhooks::RefundIdType::RefundId(id) => db .find_refund_by_merchant_id_refund_id( @@ -286,6 +289,7 @@ pub async fn find_payment_intent_from_refund_id_type( .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; db.find_payment_intent_by_payment_id_merchant_id( + &state.into(), &attempt.payment_id, &merchant_account.merchant_id, key_store, @@ -296,11 +300,12 @@ pub async fn find_payment_intent_from_refund_id_type( } pub async fn find_payment_intent_from_mandate_id_type( - db: &dyn StorageInterface, + state: &SessionState, mandate_id_type: webhooks::MandateIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; let mandate = match mandate_id_type { webhooks::MandateIdType::MandateId(mandate_id) => db .find_mandate_by_merchant_id_mandate_id( @@ -320,6 +325,7 @@ pub async fn find_payment_intent_from_mandate_id_type( .to_not_found_response(errors::ApiErrorResponse::MandateNotFound)?, }; db.find_payment_intent_by_payment_id_merchant_id( + &state.into(), &mandate .original_payment_id .ok_or(errors::ApiErrorResponse::InternalServerError) @@ -333,11 +339,12 @@ pub async fn find_payment_intent_from_mandate_id_type( } pub async fn find_mca_from_authentication_id_type( - db: &dyn StorageInterface, + state: &SessionState, authentication_id_type: webhooks::AuthenticationIdType, merchant_account: &domain::MerchantAccount, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; let authentication = match authentication_id_type { webhooks::AuthenticationIdType::AuthenticationId(authentication_id) => db .find_authentication_by_merchant_id_authentication_id( @@ -356,6 +363,7 @@ pub async fn find_mca_from_authentication_id_type( } }; db.find_by_merchant_connector_account_merchant_id_merchant_connector_id( + &state.into(), &merchant_account.merchant_id, &authentication.merchant_connector_id, key_store, @@ -367,12 +375,13 @@ pub async fn find_mca_from_authentication_id_type( } pub async fn get_mca_from_payment_intent( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &domain::MerchantAccount, payment_intent: PaymentIntent, key_store: &domain::MerchantKeyStore, connector_name: &str, ) -> CustomResult { + let db = &*state.store; let payment_attempt = db .find_payment_attempt_by_attempt_id_merchant_id( &payment_intent.active_attempt.get_id(), @@ -381,10 +390,11 @@ pub async fn get_mca_from_payment_intent( ) .await .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?; - + let key_manager_state = &state.into(); match payment_attempt.merchant_connector_id { Some(merchant_connector_id) => db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_account.merchant_id, &merchant_connector_id, key_store, @@ -410,6 +420,7 @@ pub async fn get_mca_from_payment_intent( }; db.find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, &profile_id, connector_name, key_store, @@ -426,12 +437,13 @@ pub async fn get_mca_from_payment_intent( #[cfg(feature = "payouts")] pub async fn get_mca_from_payout_attempt( - db: &dyn StorageInterface, + state: &SessionState, merchant_account: &domain::MerchantAccount, payout_id_type: webhooks::PayoutIdType, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; let payout = match payout_id_type { webhooks::PayoutIdType::PayoutAttemptId(payout_attempt_id) => db .find_payout_attempt_by_merchant_id_payout_attempt_id( @@ -450,10 +462,11 @@ pub async fn get_mca_from_payout_attempt( .await .to_not_found_response(errors::ApiErrorResponse::PayoutNotFound)?, }; - + let key_manager_state = &state.into(); match payout.merchant_connector_id { Some(merchant_connector_id) => db .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, &merchant_account.merchant_id, &merchant_connector_id, key_store, @@ -464,6 +477,7 @@ pub async fn get_mca_from_payout_attempt( }), None => db .find_merchant_connector_account_by_profile_id_connector_name( + key_manager_state, &payout.profile_id, connector_name, key_store, @@ -479,15 +493,17 @@ pub async fn get_mca_from_payout_attempt( } pub async fn get_mca_from_object_reference_id( - db: &dyn StorageInterface, + state: &SessionState, object_reference_id: webhooks::ObjectReferenceId, merchant_account: &domain::MerchantAccount, connector_name: &str, key_store: &domain::MerchantKeyStore, ) -> CustomResult { + let db = &*state.store; match merchant_account.default_profile.as_ref() { Some(profile_id) => db .find_merchant_connector_account_by_profile_id_connector_name( + &state.into(), profile_id, connector_name, key_store, @@ -499,10 +515,10 @@ pub async fn get_mca_from_object_reference_id( _ => match object_reference_id { webhooks::ObjectReferenceId::PaymentId(payment_id_type) => { get_mca_from_payment_intent( - db, + state, merchant_account, find_payment_intent_from_payment_id_type( - db, + state, payment_id_type, merchant_account, key_store, @@ -515,10 +531,10 @@ pub async fn get_mca_from_object_reference_id( } webhooks::ObjectReferenceId::RefundId(refund_id_type) => { get_mca_from_payment_intent( - db, + state, merchant_account, find_payment_intent_from_refund_id_type( - db, + state, refund_id_type, merchant_account, key_store, @@ -532,10 +548,10 @@ pub async fn get_mca_from_object_reference_id( } webhooks::ObjectReferenceId::MandateId(mandate_id_type) => { get_mca_from_payment_intent( - db, + state, merchant_account, find_payment_intent_from_mandate_id_type( - db, + state, mandate_id_type, merchant_account, key_store, @@ -548,7 +564,7 @@ pub async fn get_mca_from_object_reference_id( } webhooks::ObjectReferenceId::ExternalAuthenticationID(authentication_id_type) => { find_mca_from_authentication_id_type( - db, + state, authentication_id_type, merchant_account, key_store, @@ -558,7 +574,7 @@ pub async fn get_mca_from_object_reference_id( #[cfg(feature = "payouts")] webhooks::ObjectReferenceId::PayoutId(payout_id_type) => { get_mca_from_payout_attempt( - db, + state, merchant_account, payout_id_type, connector_name, @@ -649,13 +665,16 @@ pub fn add_connector_http_status_code_metrics(option_status_code: Option) { pub trait CustomerAddress { async fn get_address_update( &self, + state: &SessionState, address_details: payments::AddressDetails, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, + merchant_id: String, ) -> CustomResult; async fn get_domain_address( &self, + state: &SessionState, address_details: payments::AddressDetails, merchant_id: &str, customer_id: &id_type::CustomerId, @@ -668,126 +687,89 @@ pub trait CustomerAddress { impl CustomerAddress for api_models::customers::CustomerRequest { async fn get_address_update( &self, + state: &SessionState, address_details: payments::AddressDetails, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, + merchant_id: String, ) -> CustomResult { - async { - Ok(storage::AddressUpdate::Update { - city: address_details.city, - country: address_details.country, - line1: address_details - .line1 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line2: address_details - .line2 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line3: address_details - .line3 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - zip: address_details - .zip - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - state: address_details - .state - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - first_name: address_details - .first_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - last_name: address_details - .last_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - phone_number: self - .phone - .clone() - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - country_code: self.phone_country_code.clone(), - updated_by: storage_scheme.to_string(), - email: self - .email - .as_ref() - .cloned() - .async_lift(|inner| encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, - }) - } - .await + let encrypted_data = batch_encrypt( + &state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: Some(address_details.clone()), + phone_number: self.phone.clone(), + email: self.email.clone(), + }), + Identifier::Merchant(merchant_id), + key, + ) + .await?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; + Ok(storage::AddressUpdate::Update { + city: address_details.city, + country: address_details.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + zip: encryptable_address.zip, + state: encryptable_address.state, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: self.phone_country_code.clone(), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }) } async fn get_domain_address( &self, + state: &SessionState, address_details: payments::AddressDetails, merchant_id: &str, customer_id: &id_type::CustomerId, key: &[u8], storage_scheme: storage::enums::MerchantStorageScheme, ) -> CustomResult { - async { - let address = domain::Address { - id: None, - city: address_details.city, - country: address_details.country, - line1: address_details - .line1 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line2: address_details - .line2 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - line3: address_details - .line3 - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - zip: address_details - .zip - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - state: address_details - .state - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - first_name: address_details - .first_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - last_name: address_details - .last_name - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - phone_number: self - .phone - .clone() - .async_lift(|inner| encrypt_optional(inner, key)) - .await?, - country_code: self.phone_country_code.clone(), - merchant_id: merchant_id.to_string(), - address_id: generate_id(consts::ID_LENGTH, "add"), - created_at: common_utils::date_time::now(), - modified_at: common_utils::date_time::now(), - updated_by: storage_scheme.to_string(), - email: self - .email - .as_ref() - .cloned() - .async_lift(|inner| encrypt_optional(inner.map(|inner| inner.expose()), key)) - .await?, - }; + let encrypted_data = batch_encrypt( + &state.into(), + AddressDetailsWithPhone::to_encryptable(AddressDetailsWithPhone { + address: Some(address_details.clone()), + phone_number: self.phone.clone(), + email: self.email.clone(), + }), + Identifier::Merchant(merchant_id.to_string()), + key, + ) + .await?; + let encryptable_address = AddressDetailsWithPhone::from_encryptable(encrypted_data) + .change_context(common_utils::errors::CryptoError::EncodingFailed)?; + let address = domain::Address { + id: None, + city: address_details.city, + country: address_details.country, + line1: encryptable_address.line1, + line2: encryptable_address.line2, + line3: encryptable_address.line3, + zip: encryptable_address.zip, + state: encryptable_address.state, + first_name: encryptable_address.first_name, + last_name: encryptable_address.last_name, + phone_number: encryptable_address.phone_number, + country_code: self.phone_country_code.clone(), + merchant_id: merchant_id.to_string(), + address_id: generate_id(consts::ID_LENGTH, "add"), + created_at: common_utils::date_time::now(), + modified_at: common_utils::date_time::now(), + updated_by: storage_scheme.to_string(), + email: encryptable_address.email, + }; - Ok(domain::CustomerAddress { - address, - customer_id: customer_id.to_owned(), - }) - } - .await + Ok(domain::CustomerAddress { + address, + customer_id: customer_id.to_owned(), + }) } } @@ -911,7 +893,7 @@ pub async fn trigger_payments_webhook( key_store: &domain::MerchantKeyStore, payment_data: crate::core::payments::PaymentData, customer: Option, - state: &crate::routes::SessionState, + state: &SessionState, operation: Op, ) -> RouterResult<()> where diff --git a/crates/router/src/utils/connector_onboarding.rs b/crates/router/src/utils/connector_onboarding.rs index 15ad2eb89540..0bc8e58fcc45 100644 --- a/crates/router/src/utils/connector_onboarding.rs +++ b/crates/router/src/utils/connector_onboarding.rs @@ -45,9 +45,11 @@ pub async fn check_if_connector_exists( connector_id: &str, merchant_id: &str, ) -> RouterResult<()> { + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -57,6 +59,7 @@ pub async fn check_if_connector_exists( let _connector = state .store .find_by_merchant_connector_account_merchant_id_merchant_connector_id( + key_manager_state, merchant_id, connector_id, &key_store, diff --git a/crates/router/src/utils/user.rs b/crates/router/src/utils/user.rs index 3cd35886ffe3..9734122a2607 100644 --- a/crates/router/src/utils/user.rs +++ b/crates/router/src/utils/user.rs @@ -2,8 +2,8 @@ use std::{collections::HashMap, sync::Arc}; use api_models::user as user_api; use common_enums::UserAuthType; -use common_utils::errors::CustomResult; -use diesel_models::{encryption::Encryption, enums::UserStatus, user_role::UserRole}; +use common_utils::{encryption::Encryption, errors::CustomResult, types::keymanager::Identifier}; +use diesel_models::{enums::UserStatus, user_role::UserRole}; use error_stack::ResultExt; use masking::{ExposeInterface, Secret}; use redis_interface::RedisConnectionPool; @@ -33,9 +33,11 @@ impl UserFromToken { &self, state: SessionState, ) -> UserResult { + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, &self.merchant_id, &state.store.get_master_key().to_vec().into(), ) @@ -49,7 +51,7 @@ impl UserFromToken { })?; let merchant_account = state .store - .find_merchant_account_by_merchant_id(&self.merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &self.merchant_id, &key_store) .await .map_err(|e| { if e.current_context().is_db_not_found() { @@ -218,8 +220,10 @@ impl ForeignFrom<&user_api::AuthConfig> for UserAuthType { } pub async fn construct_public_and_private_db_configs( + state: &SessionState, auth_config: &user_api::AuthConfig, encryption_key: &[u8], + id: String, ) -> UserResult<(Option, Option)> { match auth_config { user_api::AuthConfig::OpenIdConnect { @@ -231,7 +235,9 @@ pub async fn construct_public_and_private_db_configs( .attach_printable("Failed to convert auth config to json")?; let encrypted_config = domain::types::encrypt::( + &state.into(), private_config_value.into(), + Identifier::UserAuth(id), encryption_key, ) .await @@ -263,6 +269,7 @@ where pub async fn decrypt_oidc_private_config( state: &SessionState, encrypted_config: Option, + id: String, ) -> UserResult { let user_auth_key = hex::decode( state @@ -277,7 +284,9 @@ pub async fn decrypt_oidc_private_config( .attach_printable("Failed to decode DEK")?; let private_config = domain::types::decrypt::( + &state.into(), encrypted_config, + Identifier::UserAuth(id), &user_auth_key, ) .await diff --git a/crates/router/src/utils/user/sample_data.rs b/crates/router/src/utils/user/sample_data.rs index b817d3e1cd74..0909f4ffbeb8 100644 --- a/crates/router/src/utils/user/sample_data.rs +++ b/crates/router/src/utils/user/sample_data.rs @@ -23,7 +23,7 @@ pub async fn generate_sample_data( ) -> SampleDataResult)>> { let merchant_id = merchant_id.to_string(); let sample_data_size: usize = req.record.unwrap_or(100); - + let key_manager_state = &state.into(); if !(10..=100).contains(&sample_data_size) { return Err(SampleDataError::InvalidRange.into()); } @@ -31,6 +31,7 @@ pub async fn generate_sample_data( let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) @@ -39,7 +40,7 @@ pub async fn generate_sample_data( let merchant_from_db = state .store - .find_merchant_account_by_merchant_id(merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, merchant_id.as_str(), &key_store) .await .change_context::(SampleDataError::DataDoesNotExist)?; diff --git a/crates/router/src/workflows/api_key_expiry.rs b/crates/router/src/workflows/api_key_expiry.rs index dc39de8f9334..e49a5f86c3c4 100644 --- a/crates/router/src/workflows/api_key_expiry.rs +++ b/crates/router/src/workflows/api_key_expiry.rs @@ -28,17 +28,22 @@ impl ProcessTrackerWorkflow for ApiKeyExpiryWorkflow { .tracking_data .clone() .parse_value("ApiKeyExpiryTrackingData")?; - + let key_manager_satte = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_satte, tracking_data.merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(tracking_data.merchant_id.as_str(), &key_store) + .find_merchant_account_by_merchant_id( + key_manager_satte, + tracking_data.merchant_id.as_str(), + &key_store, + ) .await?; let email_id = merchant_account diff --git a/crates/router/src/workflows/attach_payout_account_workflow.rs b/crates/router/src/workflows/attach_payout_account_workflow.rs index eb60510ad754..7ff0faefa681 100644 --- a/crates/router/src/workflows/attach_payout_account_workflow.rs +++ b/crates/router/src/workflows/attach_payout_account_workflow.rs @@ -31,16 +31,17 @@ impl ProcessTrackerWorkflow for AttachPayoutAccountWorkflow { .merchant_id .clone() .get_required_value("merchant_id")?; - + let key_manager_state = &state.into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_ref(), &db.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await?; let request = api::payouts::PayoutRequest::PayoutRetrieveRequest(tracking_data); diff --git a/crates/router/src/workflows/outgoing_webhook_retry.rs b/crates/router/src/workflows/outgoing_webhook_retry.rs index a8d5c28b64f2..d181aedd18f9 100644 --- a/crates/router/src/workflows/outgoing_webhook_retry.rs +++ b/crates/router/src/workflows/outgoing_webhook_retry.rs @@ -43,8 +43,10 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { .parse_value("OutgoingWebhookTrackingData")?; let db = &*state.store; + let key_manager_state = &state.into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, &tracking_data.merchant_id, &db.get_master_key().to_vec().into(), ) @@ -63,6 +65,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { let initial_event = match &tracking_data.initial_attempt_id { Some(initial_attempt_id) => { db.find_event_by_merchant_id_event_id( + key_manager_state, &business_profile.merchant_id, initial_attempt_id, &key_store, @@ -77,6 +80,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { tracking_data.primary_object_id, tracking_data.event_type ); db.find_event_by_merchant_id_event_id( + key_manager_state, &business_profile.merchant_id, &old_event_id, &key_store, @@ -106,7 +110,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { }; let event = db - .insert_event(new_event, &key_store) + .insert_event(key_manager_state, new_event, &key_store) .await .map_err(|error| { logger::error!(?error, "Failed to insert event in events table"); @@ -137,7 +141,11 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { // resource None => { let merchant_account = db - .find_merchant_account_by_merchant_id(&tracking_data.merchant_id, &key_store) + .find_merchant_account_by_merchant_id( + key_manager_state, + &tracking_data.merchant_id, + &key_store, + ) .await?; // TODO: Add request state for the PT flows as well @@ -162,6 +170,7 @@ impl ProcessTrackerWorkflow for OutgoingWebhookRetryWorkflow { }; let request_content = webhooks_core::get_outgoing_webhook_request( + state, &merchant_account, outgoing_webhook, &business_profile, diff --git a/crates/router/src/workflows/payment_method_status_update.rs b/crates/router/src/workflows/payment_method_status_update.rs index b8e57360d90f..fa0631cc5c26 100644 --- a/crates/router/src/workflows/payment_method_status_update.rs +++ b/crates/router/src/workflows/payment_method_status_update.rs @@ -30,17 +30,18 @@ impl ProcessTrackerWorkflow for PaymentMethodStatusUpdateWorkflow let prev_pm_status = tracking_data.prev_status; let curr_pm_status = tracking_data.curr_status; let merchant_id = tracking_data.merchant_id; - + let key_manager_state = &state.into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, merchant_id.as_str(), &state.store.get_master_key().to_vec().into(), ) .await?; let merchant_account = db - .find_merchant_account_by_merchant_id(&merchant_id, &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, &merchant_id, &key_store) .await?; let payment_method = db diff --git a/crates/router/src/workflows/payment_sync.rs b/crates/router/src/workflows/payment_sync.rs index ef0fe07e9b75..2bc7b183ec15 100644 --- a/crates/router/src/workflows/payment_sync.rs +++ b/crates/router/src/workflows/payment_sync.rs @@ -38,9 +38,10 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { .tracking_data .clone() .parse_value("PaymentsRetrieveRequest")?; - + let key_manager_state = &state.into(); let key_store = db .get_merchant_key_store_by_merchant_id( + key_manager_state, tracking_data .merchant_id .as_ref() @@ -51,6 +52,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { let merchant_account = db .find_merchant_account_by_merchant_id( + key_manager_state, tracking_data .merchant_id .as_ref() @@ -148,6 +150,7 @@ impl ProcessTrackerWorkflow for PaymentsSyncWorkflow { payment_data.payment_intent = db .update_payment_intent( + &state.into(), payment_data.payment_intent, payment_intent_update, &key_store, diff --git a/crates/router/tests/payments.rs b/crates/router/tests/payments.rs index ed11f3b8b1cb..997ae51cf31f 100644 --- a/crates/router/tests/payments.rs +++ b/crates/router/tests/payments.rs @@ -288,10 +288,11 @@ async fn payments_create_core() { let state = Arc::new(app_state) .get_session_state("public", || {}) .unwrap(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -300,7 +301,7 @@ async fn payments_create_core() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); @@ -476,10 +477,11 @@ async fn payments_create_core_adyen_no_redirect() { let customer_id = format!("cust_{}", Uuid::new_v4()); let merchant_id = "arunraj".to_string(); let payment_id = "pay_mbabizu24mvu3mela5njyhpit10".to_string(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -488,7 +490,7 @@ async fn payments_create_core_adyen_no_redirect() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); diff --git a/crates/router/tests/payments2.rs b/crates/router/tests/payments2.rs index 4c178e4122bb..fcf106a9efc6 100644 --- a/crates/router/tests/payments2.rs +++ b/crates/router/tests/payments2.rs @@ -48,10 +48,11 @@ async fn payments_create_core() { let state = Arc::new(app_state) .get_session_state("public", || {}) .unwrap(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -60,7 +61,7 @@ async fn payments_create_core() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); @@ -243,10 +244,11 @@ async fn payments_create_core_adyen_no_redirect() { let customer_id = format!("cust_{}", Uuid::new_v4()); let merchant_id = "arunraj".to_string(); let payment_id = "pay_mbabizu24mvu3mela5njyhpit10".to_string(); - + let key_manager_state = &(&state).into(); let key_store = state .store .get_merchant_key_store_by_merchant_id( + key_manager_state, "juspay_merchant", &state.store.get_master_key().to_vec().into(), ) @@ -255,7 +257,7 @@ async fn payments_create_core_adyen_no_redirect() { let merchant_account = state .store - .find_merchant_account_by_merchant_id("juspay_merchant", &key_store) + .find_merchant_account_by_merchant_id(key_manager_state, "juspay_merchant", &key_store) .await .unwrap(); diff --git a/crates/storage_impl/src/mock_db/payment_intent.rs b/crates/storage_impl/src/mock_db/payment_intent.rs index bf3ce0baaab0..3e183b94c26e 100644 --- a/crates/storage_impl/src/mock_db/payment_intent.rs +++ b/crates/storage_impl/src/mock_db/payment_intent.rs @@ -1,4 +1,4 @@ -use common_utils::errors::CustomResult; +use common_utils::{errors::CustomResult, types::keymanager::KeyManagerState}; use diesel_models::enums as storage_enums; use error_stack::ResultExt; use hyperswitch_domain_models::{ @@ -19,6 +19,7 @@ impl PaymentIntentInterface for MockDb { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + _state: &KeyManagerState, _merchant_id: &str, _filters: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, _key_store: &MerchantKeyStore, @@ -30,6 +31,7 @@ impl PaymentIntentInterface for MockDb { #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + _state: &KeyManagerState, _merchant_id: &str, _time_range: &api_models::payments::TimeRange, _key_store: &MerchantKeyStore, @@ -51,6 +53,7 @@ impl PaymentIntentInterface for MockDb { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + _state: &KeyManagerState, _merchant_id: &str, _constraints: &hyperswitch_domain_models::payments::payment_intent::PaymentIntentFetchConstraints, _key_store: &MerchantKeyStore, @@ -63,6 +66,7 @@ impl PaymentIntentInterface for MockDb { #[allow(clippy::panic)] async fn insert_payment_intent( &self, + _state: &KeyManagerState, new: PaymentIntent, _key_store: &MerchantKeyStore, _storage_scheme: storage_enums::MerchantStorageScheme, @@ -76,6 +80,7 @@ impl PaymentIntentInterface for MockDb { #[allow(clippy::unwrap_used)] async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, update: PaymentIntentUpdate, key_store: &MerchantKeyStore, @@ -95,8 +100,10 @@ impl PaymentIntentInterface for MockDb { .change_context(StorageError::EncryptionError)?; *payment_intent = PaymentIntent::convert_back( + state, diesel_payment_intent_update.apply_changeset(diesel_payment_intent), key_store.key.get_inner(), + key_store.merchant_id.clone(), ) .await .change_context(StorageError::DecryptionError)?; @@ -108,6 +115,7 @@ impl PaymentIntentInterface for MockDb { #[allow(clippy::unwrap_used)] async fn find_payment_intent_by_payment_id_merchant_id( &self, + _state: &KeyManagerState, payment_id: &str, merchant_id: &str, _key_store: &MerchantKeyStore, diff --git a/crates/storage_impl/src/payments/payment_intent.rs b/crates/storage_impl/src/payments/payment_intent.rs index 62854cb261a2..ced94b5717b0 100644 --- a/crates/storage_impl/src/payments/payment_intent.rs +++ b/crates/storage_impl/src/payments/payment_intent.rs @@ -4,7 +4,10 @@ use api_models::payments::AmountFilter; use async_bb8_diesel::{AsyncConnection, AsyncRunQueryDsl}; #[cfg(feature = "olap")] use common_utils::errors::ReportSwitchExt; -use common_utils::ext_traits::{AsyncExt, Encode}; +use common_utils::{ + ext_traits::{AsyncExt, Encode}, + types::keymanager::KeyManagerState, +}; #[cfg(feature = "olap")] use diesel::{associations::HasTable, ExpressionMethods, JoinOnDsl, QueryDsl}; use diesel_models::{ @@ -53,6 +56,7 @@ use crate::{ impl PaymentIntentInterface for KVRouterStore { async fn insert_payment_intent( &self, + state: &KeyManagerState, payment_intent: PaymentIntent, merchant_key_store: &MerchantKeyStore, storage_scheme: MerchantStorageScheme, @@ -69,7 +73,12 @@ impl PaymentIntentInterface for KVRouterStore { match storage_scheme { MerchantStorageScheme::PostgresOnly => { self.router_store - .insert_payment_intent(payment_intent, merchant_key_store, storage_scheme) + .insert_payment_intent( + state, + payment_intent, + merchant_key_store, + storage_scheme, + ) .await } @@ -121,6 +130,7 @@ impl PaymentIntentInterface for KVRouterStore { #[instrument(skip_all)] async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, payment_intent_update: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, @@ -143,6 +153,7 @@ impl PaymentIntentInterface for KVRouterStore { MerchantStorageScheme::PostgresOnly => { self.router_store .update_payment_intent( + state, this, payment_intent_update, merchant_key_store, @@ -189,10 +200,14 @@ impl PaymentIntentInterface for KVRouterStore { .try_into_hset() .change_context(StorageError::KVError)?; - let payment_intent = - PaymentIntent::convert_back(diesel_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError)?; + let payment_intent = PaymentIntent::convert_back( + state, + diesel_intent, + merchant_key_store.key.get_inner(), + merchant_id, + ) + .await + .change_context(StorageError::DecryptionError)?; Ok(payment_intent) } @@ -202,6 +217,7 @@ impl PaymentIntentInterface for KVRouterStore { #[instrument(skip_all)] async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, merchant_key_store: &MerchantKeyStore, @@ -243,9 +259,14 @@ impl PaymentIntentInterface for KVRouterStore { } }?; - PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError) + PaymentIntent::convert_back( + state, + diesel_payment_intent, + merchant_key_store.key.get_inner(), + merchant_id.to_string(), + ) + .await + .change_context(StorageError::DecryptionError) } async fn get_active_payment_attempt( @@ -278,6 +299,7 @@ impl PaymentIntentInterface for KVRouterStore { #[cfg(feature = "olap")] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -285,6 +307,7 @@ impl PaymentIntentInterface for KVRouterStore { ) -> error_stack::Result, StorageError> { self.router_store .filter_payment_intent_by_constraints( + state, merchant_id, filters, merchant_key_store, @@ -296,6 +319,7 @@ impl PaymentIntentInterface for KVRouterStore { #[cfg(feature = "olap")] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, merchant_key_store: &MerchantKeyStore, @@ -303,6 +327,7 @@ impl PaymentIntentInterface for KVRouterStore { ) -> error_stack::Result, StorageError> { self.router_store .filter_payment_intents_by_time_range_constraints( + state, merchant_id, time_range, merchant_key_store, @@ -314,6 +339,7 @@ impl PaymentIntentInterface for KVRouterStore { #[cfg(feature = "olap")] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -321,6 +347,7 @@ impl PaymentIntentInterface for KVRouterStore { ) -> error_stack::Result, StorageError> { self.router_store .get_filtered_payment_intents_attempt( + state, merchant_id, filters, merchant_key_store, @@ -351,6 +378,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn insert_payment_intent( &self, + state: &KeyManagerState, payment_intent: PaymentIntent, merchant_key_store: &MerchantKeyStore, _storage_scheme: MerchantStorageScheme, @@ -367,14 +395,20 @@ impl PaymentIntentInterface for crate::RouterStore { er.change_context(new_err) })?; - PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError) + PaymentIntent::convert_back( + state, + diesel_payment_intent, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) + .await + .change_context(StorageError::DecryptionError) } #[instrument(skip_all)] async fn update_payment_intent( &self, + state: &KeyManagerState, this: PaymentIntent, payment_intent: PaymentIntentUpdate, merchant_key_store: &MerchantKeyStore, @@ -394,14 +428,20 @@ impl PaymentIntentInterface for crate::RouterStore { er.change_context(new_err) })?; - PaymentIntent::convert_back(diesel_payment_intent, merchant_key_store.key.get_inner()) - .await - .change_context(StorageError::DecryptionError) + PaymentIntent::convert_back( + state, + diesel_payment_intent, + merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), + ) + .await + .change_context(StorageError::DecryptionError) } #[instrument(skip_all)] async fn find_payment_intent_by_payment_id_merchant_id( &self, + state: &KeyManagerState, payment_id: &str, merchant_id: &str, merchant_key_store: &MerchantKeyStore, @@ -417,8 +457,10 @@ impl PaymentIntentInterface for crate::RouterStore { }) .async_and_then(|diesel_payment_intent| async { PaymentIntent::convert_back( + state, diesel_payment_intent, merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), ) .await .change_context(StorageError::DecryptionError) @@ -458,6 +500,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn filter_payment_intent_by_constraints( &self, + state: &KeyManagerState, merchant_id: &str, filters: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -498,6 +541,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let starting_at = self .find_payment_intent_by_payment_id_merchant_id( + state, starting_after_id, merchant_id, merchant_key_store, @@ -516,6 +560,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let ending_at = self .find_payment_intent_by_payment_id_merchant_id( + state, ending_before_id, merchant_id, merchant_key_store, @@ -560,8 +605,10 @@ impl PaymentIntentInterface for crate::RouterStore { .map(|payment_intents| { try_join_all(payment_intents.into_iter().map(|diesel_payment_intent| { PaymentIntent::convert_back( + state, diesel_payment_intent, merchant_key_store.key.get_inner(), + merchant_key_store.merchant_id.clone(), ) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) @@ -579,6 +626,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn filter_payment_intents_by_time_range_constraints( &self, + state: &KeyManagerState, merchant_id: &str, time_range: &api_models::payments::TimeRange, merchant_key_store: &MerchantKeyStore, @@ -587,6 +635,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Remove this redundant function let payment_filters = (*time_range).into(); self.filter_payment_intent_by_constraints( + state, merchant_id, &payment_filters, merchant_key_store, @@ -599,6 +648,7 @@ impl PaymentIntentInterface for crate::RouterStore { #[instrument(skip_all)] async fn get_filtered_payment_intents_attempt( &self, + state: &KeyManagerState, merchant_id: &str, constraints: &PaymentIntentFetchConstraints, merchant_key_store: &MerchantKeyStore, @@ -640,6 +690,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let starting_at = self .find_payment_intent_by_payment_id_merchant_id( + state, starting_after_id, merchant_id, merchant_key_store, @@ -658,6 +709,7 @@ impl PaymentIntentInterface for crate::RouterStore { // TODO: Fetch partial columns for this query since we only need some columns let ending_at = self .find_payment_intent_by_payment_id_merchant_id( + state, ending_before_id, merchant_id, merchant_key_store, @@ -745,13 +797,17 @@ impl PaymentIntentInterface for crate::RouterStore { .await .map(|results| { try_join_all(results.into_iter().map(|(pi, pa)| { - PaymentIntent::convert_back(pi, merchant_key_store.key.get_inner()).map( - |payment_intent| { - payment_intent.map(|payment_intent| { - (payment_intent, PaymentAttempt::from_storage_model(pa)) - }) - }, + PaymentIntent::convert_back( + state, + pi, + merchant_key_store.key.get_inner(), + merchant_id.to_string(), ) + .map(|payment_intent| { + payment_intent.map(|payment_intent| { + (payment_intent, PaymentAttempt::from_storage_model(pa)) + }) + }) })) .map(|join_result| join_result.change_context(StorageError::DecryptionError)) }) From eb01680284fea4d61ef95418878d49104885352e Mon Sep 17 00:00:00 2001 From: Sarthak Soni <76486416+Sarthak1799@users.noreply.github.com> Date: Fri, 19 Jul 2024 13:35:27 +0530 Subject: [PATCH 23/26] feat(connector): Plaid connector Integration (#3952) Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com> --- config/config.example.toml | 4 + config/deployments/integration_test.toml | 4 + config/deployments/production.toml | 4 + config/deployments/sandbox.toml | 4 + config/development.toml | 5 + config/docker_compose.toml | 5 + .../src/router_request_types.rs | 6 + crates/hyperswitch_interfaces/src/configs.rs | 1 + .../src/connector/plaid/transformers.rs | 11 - crates/pm_auth/src/types.rs | 5 - crates/router/src/connector.rs | 3 +- crates/router/src/connector/plaid.rs | 458 ++++++++++++++++++ .../src/connector/plaid/transformers.rs | 389 +++++++++++++++ crates/router/src/core/admin.rs | 4 +- .../connector_integration_v2_impls.rs | 75 ++- crates/router/src/core/payments/flows.rs | 31 ++ crates/router/src/core/pm_auth.rs | 1 - crates/router/src/types/api.rs | 4 +- crates/router/src/types/domain/payments.rs | 8 +- crates/router/src/types/transformers.rs | 6 +- crates/router/tests/connectors/main.rs | 1 + crates/router/tests/connectors/plaid.rs | 421 ++++++++++++++++ .../router/tests/connectors/sample_auth.toml | 4 + crates/test_utils/src/connector_auth.rs | 1 + loadtest/config/development.toml | 2 + scripts/add_connector.sh | 2 +- 26 files changed, 1403 insertions(+), 56 deletions(-) create mode 100644 crates/router/src/connector/plaid.rs create mode 100644 crates/router/src/connector/plaid/transformers.rs create mode 100644 crates/router/tests/connectors/plaid.rs diff --git a/config/config.example.toml b/config/config.example.toml index 443a805da0bc..5c99bf1cfb2c 100644 --- a/config/config.example.toml +++ b/config/config.example.toml @@ -233,6 +233,7 @@ payone.base_url = "https://payment.preprod.payone.com/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" placetopay.base_url = "https://test.placetopay.com/rest/gateway" +plaid.base_url = "https://sandbox.plaid.com" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" @@ -488,6 +489,9 @@ open_banking_uk = { country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,M [pm_filters.razorpay] upi_collect = { country = "IN", currency = "INR" } +[pm_filters.plaid] +open_banking_pis = {currency = "EUR,GBP"} + [pm_filters.zen] credit = { not_available_flows = { capture_method = "manual" } } debit = { not_available_flows = { capture_method = "manual" } } diff --git a/config/deployments/integration_test.toml b/config/deployments/integration_test.toml index fbcd7cf0364c..277fee3c6e51 100644 --- a/config/deployments/integration_test.toml +++ b/config/deployments/integration_test.toml @@ -72,6 +72,7 @@ payone.base_url = "https://payment.preprod.payone.com/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" placetopay.base_url = "https://test.placetopay.com/rest/gateway" +plaid.base_url = "https://sandbox.plaid.com" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" @@ -293,6 +294,9 @@ open_banking_uk = {country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,MT [pm_filters.razorpay] upi_collect = {country = "IN", currency = "INR"} +[pm_filters.plaid] +open_banking_pis = {currency = "EUR,GBP"} + [pm_filters.worldpay] apple_pay.country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US" google_pay.country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" diff --git a/config/deployments/production.toml b/config/deployments/production.toml index e3b84396bb3a..9cb98ad4cb25 100644 --- a/config/deployments/production.toml +++ b/config/deployments/production.toml @@ -76,6 +76,7 @@ payone.base_url = "https://payment.payone.com/" paypal.base_url = "https://api-m.paypal.com/" payu.base_url = "https://secure.payu.com/api/" placetopay.base_url = "https://checkout.placetopay.com/rest/gateway" +plaid.base_url = "https://production.plaid.com" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" @@ -312,6 +313,9 @@ open_banking_uk = {country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,MT [pm_filters.razorpay] upi_collect = {country = "IN", currency = "INR"} +[pm_filters.plaid] +open_banking_pis = {currency = "EUR,GBP"} + [pm_filters.worldpay] apple_pay.country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US" google_pay.country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" diff --git a/config/deployments/sandbox.toml b/config/deployments/sandbox.toml index 2f428aa5c072..8c19767947c7 100644 --- a/config/deployments/sandbox.toml +++ b/config/deployments/sandbox.toml @@ -76,6 +76,7 @@ payone.base_url = "https://payment.preprod.payone.com/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" placetopay.base_url = "https://test.placetopay.com/rest/gateway" +plaid.base_url = "https://sandbox.plaid.com" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" @@ -316,6 +317,9 @@ open_banking_uk = { country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,M [pm_filters.razorpay] upi_collect = {country = "IN", currency = "INR"} +[pm_filters.plaid] +open_banking_pis = {currency = "EUR,GBP"} + [pm_filters.worldpay] apple_pay.country = "AU,CN,HK,JP,MO,MY,NZ,SG,TW,AM,AT,AZ,BY,BE,BG,HR,CY,CZ,DK,EE,FO,FI,FR,GE,DE,GR,GL,GG,HU,IS,IE,IM,IT,KZ,JE,LV,LI,LT,LU,MT,MD,MC,ME,NL,NO,PL,PT,RO,SM,RS,SK,SI,ES,SE,CH,UA,GB,AR,CO,CR,BR,MX,PE,BH,IL,JO,KW,PS,QA,SA,AE,CA,UM,US" google_pay.country = "AL,DZ,AS,AO,AG,AR,AU,AT,AZ,BH,BY,BE,BR,BG,CA,CL,CO,HR,CZ,DK,DO,EG,EE,FI,FR,DE,GR,HK,HU,IN,ID,IE,IL,IT,JP,JO,KZ,KE,KW,LV,LB,LT,LU,MY,MX,NL,NZ,NO,OM,PK,PA,PE,PH,PL,PT,QA,RO,RU,SA,SG,SK,ZA,ES,LK,SE,CH,TW,TH,TR,UA,AE,GB,US,UY,VN" diff --git a/config/development.toml b/config/development.toml index dd7859908d4e..7a90ccec5030 100644 --- a/config/development.toml +++ b/config/development.toml @@ -141,6 +141,7 @@ cards = [ "paypal", "payu", "placetopay", + "plaid", "powertranz", "prophetpay", "shift4", @@ -227,6 +228,7 @@ payone.base_url = "https://payment.preprod.payone.com/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" placetopay.base_url = "https://test.placetopay.com/rest/gateway" +plaid.base_url = "https://sandbox.plaid.com" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" @@ -345,6 +347,9 @@ open_banking_uk = { country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,M [pm_filters.razorpay] upi_collect = {country = "IN", currency = "INR"} +[pm_filters.plaid] +open_banking_pis = {currency = "EUR,GBP"} + [pm_filters.adyen] google_pay = { country = "AU,NZ,JP,HK,SG,MY,TH,VN,BH,AE,KW,BR,ES,GB,SE,NO,SK,AT,NL,DE,HU,CY,LU,CH,BE,FR,DK,RO,HR,LI,MT,SI,GR,PT,IE,CZ,EE,LT,LV,IT,PL,TR,IS,CA,US", currency = "AED,ALL,AMD,ANG,AOA,ARS,AUD,AWG,AZN,BAM,BBD,BDT,BGN,BHD,BMD,BND,BOB,BRL,BSD,BWP,BYN,BZD,CAD,CHF,CLP,CNY,COP,CRC,CUP,CVE,CZK,DJF,DKK,DOP,DZD,EGP,ETB,EUR,FJD,FKP,GBP,GEL,GHS,GIP,GMD,GNF,GTQ,GYD,HKD,HNL,HTG,HUF,IDR,ILS,INR,IQD,JMD,JOD,JPY,KES,KGS,KHR,KMF,KRW,KWD,KYD,KZT,LAK,LBP,LKR,LYD,MAD,MDL,MKD,MMK,MNT,MOP,MRU,MUR,MVR,MWK,MXN,MYR,MZN,NAD,NGN,NIO,NOK,NPR,NZD,OMR,PAB,PEN,PGK,PHP,PKR,PLN,PYG,QAR,RON,RSD,RUB,RWF,SAR,SBD,SCR,SEK,SGD,SHP,SLE,SOS,SRD,STN,SVC,SZL,THB,TND,TOP,TRY,TTD,TWD,TZS,UAH,UGX,USD,UYU,UZS,VES,VND,VUV,WST,XAF,XCD,XOF,XPF,YER,ZAR,ZMW" } apple_pay = { country = "AU,NZ,CN,JP,HK,SG,MY,BH,AE,KW,BR,ES,GB,SE,NO,AT,NL,DE,HU,CY,LU,CH,BE,FR,DK,FI,RO,HR,LI,UA,MT,SI,GR,PT,IE,CZ,EE,LT,LV,IT,PL,IS,CA,US", currency = "AUD,CHF,CAD,EUR,GBP,HKD,SGD,USD" } diff --git a/config/docker_compose.toml b/config/docker_compose.toml index a6260055b716..7ce3bc646c55 100644 --- a/config/docker_compose.toml +++ b/config/docker_compose.toml @@ -162,6 +162,7 @@ payone.base_url = "https://payment.preprod.payone.com/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" placetopay.base_url = "https://test.placetopay.com/rest/gateway" +plaid.base_url = "https://sandbox.plaid.com" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" @@ -239,6 +240,7 @@ cards = [ "paypal", "payu", "placetopay", + "plaid", "powertranz", "prophetpay", "shift4", @@ -355,6 +357,9 @@ open_banking_uk = { country = "DE,GB,AT,BE,CY,EE,ES,FI,FR,GR,HR,IE,IT,LT,LU,LV,M [pm_filters.razorpay] upi_collect = { country = "IN", currency = "INR" } +[pm_filters.plaid] +open_banking_pis = {currency = "EUR,GBP"} + [pm_filters.zen] credit = { not_available_flows = { capture_method = "manual" } } debit = { not_available_flows = { capture_method = "manual" } } diff --git a/crates/hyperswitch_domain_models/src/router_request_types.rs b/crates/hyperswitch_domain_models/src/router_request_types.rs index 825927d021ab..808000e8dc89 100644 --- a/crates/hyperswitch_domain_models/src/router_request_types.rs +++ b/crates/hyperswitch_domain_models/src/router_request_types.rs @@ -342,6 +342,7 @@ pub struct PaymentsPostProcessingData { pub payment_method_data: PaymentMethodData, pub customer_id: Option, pub connector_transaction_id: Option, + pub country: Option, } impl TryFrom> @@ -362,6 +363,11 @@ impl TryFrom None, }, customer_id: data.request.customer_id, + country: data + .address + .get_payment_billing() + .and_then(|bl| bl.address.as_ref()) + .and_then(|address| address.country), }) } } diff --git a/crates/hyperswitch_interfaces/src/configs.rs b/crates/hyperswitch_interfaces/src/configs.rs index f9fb992d2553..3072a952b15b 100644 --- a/crates/hyperswitch_interfaces/src/configs.rs +++ b/crates/hyperswitch_interfaces/src/configs.rs @@ -62,6 +62,7 @@ pub struct Connectors { pub paypal: ConnectorParams, pub payu: ConnectorParams, pub placetopay: ConnectorParams, + pub plaid: ConnectorParams, pub powertranz: ConnectorParams, pub prophetpay: ConnectorParams, pub rapyd: ConnectorParams, diff --git a/crates/pm_auth/src/connector/plaid/transformers.rs b/crates/pm_auth/src/connector/plaid/transformers.rs index 571f27f5b20b..109cc5a5c15a 100644 --- a/crates/pm_auth/src/connector/plaid/transformers.rs +++ b/crates/pm_auth/src/connector/plaid/transformers.rs @@ -448,7 +448,6 @@ impl pub struct PlaidAuthType { pub client_id: Secret, pub secret: Secret, - pub merchant_data: Option, } impl TryFrom<&types::ConnectorAuthType> for PlaidAuthType { @@ -458,16 +457,6 @@ impl TryFrom<&types::ConnectorAuthType> for PlaidAuthType { types::ConnectorAuthType::BodyKey { client_id, secret } => Ok(Self { client_id: client_id.to_owned(), secret: secret.to_owned(), - merchant_data: None, - }), - types::ConnectorAuthType::OpenBankingAuth { - api_key, - key1, - merchant_data, - } => Ok(Self { - client_id: api_key.to_owned(), - secret: key1.to_owned(), - merchant_data: Some(merchant_data.clone()), }), _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), } diff --git a/crates/pm_auth/src/types.rs b/crates/pm_auth/src/types.rs index 7450251f157a..e5fb8fb6441f 100644 --- a/crates/pm_auth/src/types.rs +++ b/crates/pm_auth/src/types.rs @@ -218,11 +218,6 @@ pub enum ConnectorAuthType { client_id: Secret, secret: Secret, }, - OpenBankingAuth { - api_key: Secret, - key1: Secret, - merchant_data: MerchantRecipientData, - }, #[default] NoKey, } diff --git a/crates/router/src/connector.rs b/crates/router/src/connector.rs index 341a9215cd31..85f51bcc1107 100644 --- a/crates/router/src/connector.rs +++ b/crates/router/src/connector.rs @@ -47,6 +47,7 @@ pub mod payone; pub mod paypal; pub mod payu; pub mod placetopay; +pub mod plaid; pub mod powertranz; pub mod prophetpay; pub mod rapyd; @@ -81,7 +82,7 @@ pub use self::{ iatapay::Iatapay, itaubank::Itaubank, klarna::Klarna, mifinity::Mifinity, mollie::Mollie, multisafepay::Multisafepay, netcetera::Netcetera, nexinets::Nexinets, nmi::Nmi, noon::Noon, nuvei::Nuvei, opayo::Opayo, opennode::Opennode, payeezy::Payeezy, payme::Payme, payone::Payone, - paypal::Paypal, payu::Payu, placetopay::Placetopay, powertranz::Powertranz, + paypal::Paypal, payu::Payu, placetopay::Placetopay, plaid::Plaid, powertranz::Powertranz, prophetpay::Prophetpay, rapyd::Rapyd, razorpay::Razorpay, riskified::Riskified, shift4::Shift4, signifyd::Signifyd, square::Square, stax::Stax, stripe::Stripe, threedsecureio::Threedsecureio, trustpay::Trustpay, tsys::Tsys, volt::Volt, wise::Wise, worldline::Worldline, diff --git a/crates/router/src/connector/plaid.rs b/crates/router/src/connector/plaid.rs new file mode 100644 index 000000000000..5bfd42994262 --- /dev/null +++ b/crates/router/src/connector/plaid.rs @@ -0,0 +1,458 @@ +pub mod transformers; + +use common_utils::types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector}; +use error_stack::ResultExt; +use transformers as plaid; + +use crate::{ + configs::settings, + connector::utils as connector_utils, + core::errors::{self, CustomResult}, + events::connector_api_logs::ConnectorEvent, + headers, + services::{ + self, + request::{self, Mask}, + ConnectorIntegration, ConnectorValidation, + }, + types::{ + self, + api::{self, ConnectorCommon, ConnectorCommonExt}, + ErrorResponse, RequestContent, Response, + }, + utils::BytesExt, +}; + +#[derive(Clone)] +pub struct Plaid { + amount_converter: &'static (dyn AmountConvertor + Sync), +} + +impl Plaid { + pub fn new() -> &'static Self { + &Self { + amount_converter: &FloatMajorUnitForConnector, + } + } +} + +impl api::Payment for Plaid {} +impl api::PaymentSession for Plaid {} +impl api::ConnectorAccessToken for Plaid {} +impl api::MandateSetup for Plaid {} +impl api::PaymentAuthorize for Plaid {} +impl api::PaymentSync for Plaid {} +impl api::PaymentCapture for Plaid {} +impl api::PaymentVoid for Plaid {} +impl api::Refund for Plaid {} +impl api::RefundExecute for Plaid {} +impl api::RefundSync for Plaid {} +impl api::PaymentToken for Plaid {} +impl api::PaymentsPostProcessing for Plaid {} + +impl + ConnectorIntegration< + api::PaymentMethodToken, + types::PaymentMethodTokenizationData, + types::PaymentsResponseData, + > for Plaid +{ + // Not Implemented (R) +} + +impl ConnectorCommonExt for Plaid +where + Self: ConnectorIntegration, +{ + fn build_headers( + &self, + req: &types::RouterData, + _connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + let mut header = vec![( + headers::CONTENT_TYPE.to_string(), + self.get_content_type().to_string().into(), + )]; + let mut auth = self.get_auth_header(&req.connector_auth_type)?; + header.append(&mut auth); + Ok(header) + } +} + +impl ConnectorCommon for Plaid { + fn id(&self) -> &'static str { + "plaid" + } + + fn get_currency_unit(&self) -> api::CurrencyUnit { + api::CurrencyUnit::Base + } + + fn common_get_content_type(&self) -> &'static str { + "application/json" + } + + fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str { + connectors.plaid.base_url.as_ref() + } + + fn get_auth_header( + &self, + auth_type: &types::ConnectorAuthType, + ) -> CustomResult)>, errors::ConnectorError> { + let auth = plaid::PlaidAuthType::try_from(auth_type) + .change_context(errors::ConnectorError::FailedToObtainAuthType)?; + let client_id = auth.client_id.into_masked(); + let secret = auth.secret.into_masked(); + + Ok(vec![ + ("PLAID-CLIENT-ID".to_string(), client_id), + ("PLAID-SECRET".to_string(), secret), + ]) + } + + fn build_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + let response: plaid::PlaidErrorResponse = + res.response + .parse_struct("PlaidErrorResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + + Ok(ErrorResponse { + status_code: res.status_code, + code: response + .error_code + .unwrap_or(crate::consts::NO_ERROR_CODE.to_string()), + message: response.error_message, + reason: response.display_message, + attempt_status: None, + connector_transaction_id: None, + }) + } +} + +impl ConnectorValidation for Plaid { + //TODO: implement functions when support enabled +} + +impl ConnectorIntegration + for Plaid +{ + //TODO: implement sessions flow +} + +impl ConnectorIntegration + for Plaid +{ +} + +impl + ConnectorIntegration< + api::SetupMandate, + types::SetupMandateRequestData, + types::PaymentsResponseData, + > for Plaid +{ +} + +impl ConnectorIntegration + for Plaid +{ + fn get_headers( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!( + "{}/payment_initiation/payment/create", + self.base_url(connectors) + )) + } + + fn get_request_body( + &self, + req: &types::PaymentsAuthorizeRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let amount = connector_utils::convert_amount( + self.amount_converter, + req.request.minor_amount, + req.request.currency, + )?; + let connector_router_data = plaid::PlaidRouterData::from((amount, req)); + let connector_req = plaid::PlaidPaymentsRequest::try_from(&connector_router_data)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::PaymentsAuthorizeRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsAuthorizeType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsAuthorizeType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsAuthorizeType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsAuthorizeRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: plaid::PlaidPaymentsResponse = res + .response + .parse_struct("PlaidPaymentsResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Plaid +{ + fn get_headers( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_request_body( + &self, + req: &types::PaymentsSyncRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let connector_req = plaid::PlaidSyncRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn get_url( + &self, + _req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!( + "{}/payment_initiation/payment/get", + self.base_url(connectors) + )) + } + + fn build_request( + &self, + req: &types::PaymentsSyncRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsSyncType::get_url(self, req, connectors)?) + .attach_default_headers() + .headers(types::PaymentsSyncType::get_headers(self, req, connectors)?) + .set_body(types::PaymentsSyncType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsSyncRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: plaid::PlaidSyncResponse = res + .response + .parse_struct("PlaidSyncResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl + ConnectorIntegration< + api::PostProcessing, + types::PaymentsPostProcessingData, + types::PaymentsResponseData, + > for Plaid +{ + fn get_headers( + &self, + req: &types::PaymentsPostProcessingRouterData, + connectors: &settings::Connectors, + ) -> CustomResult)>, errors::ConnectorError> { + self.build_headers(req, connectors) + } + + fn get_content_type(&self) -> &'static str { + self.common_get_content_type() + } + + fn get_url( + &self, + _req: &types::PaymentsPostProcessingRouterData, + connectors: &settings::Connectors, + ) -> CustomResult { + Ok(format!("{}/link/token/create", self.base_url(connectors))) + } + + fn get_request_body( + &self, + req: &types::PaymentsPostProcessingRouterData, + _connectors: &settings::Connectors, + ) -> CustomResult { + let connector_req = plaid::PlaidLinkTokenRequest::try_from(req)?; + Ok(RequestContent::Json(Box::new(connector_req))) + } + + fn build_request( + &self, + req: &types::PaymentsPostProcessingRouterData, + connectors: &settings::Connectors, + ) -> CustomResult, errors::ConnectorError> { + Ok(Some( + services::RequestBuilder::new() + .method(services::Method::Post) + .url(&types::PaymentsPostProcessingType::get_url( + self, req, connectors, + )?) + .attach_default_headers() + .headers(types::PaymentsPostProcessingType::get_headers( + self, req, connectors, + )?) + .set_body(types::PaymentsPostProcessingType::get_request_body( + self, req, connectors, + )?) + .build(), + )) + } + + fn handle_response( + &self, + data: &types::PaymentsPostProcessingRouterData, + event_builder: Option<&mut ConnectorEvent>, + res: Response, + ) -> CustomResult { + let response: plaid::PlaidLinkTokenResponse = res + .response + .parse_struct("PlaidLinkTokenResponse") + .change_context(errors::ConnectorError::ResponseDeserializationFailed)?; + event_builder.map(|i| i.set_response_body(&response)); + router_env::logger::info!(connector_response=?response); + types::RouterData::try_from(types::ResponseRouterData { + response, + data: data.clone(), + http_code: res.status_code, + }) + } + + fn get_error_response( + &self, + res: Response, + event_builder: Option<&mut ConnectorEvent>, + ) -> CustomResult { + self.build_error_response(res, event_builder) + } +} + +impl ConnectorIntegration + for Plaid +{ +} + +impl ConnectorIntegration + for Plaid +{ +} + +impl ConnectorIntegration for Plaid {} + +impl ConnectorIntegration for Plaid {} + +#[async_trait::async_trait] +impl api::IncomingWebhook for Plaid { + fn get_webhook_object_reference_id( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err((errors::ConnectorError::WebhooksNotImplemented).into()) + } + + fn get_webhook_event_type( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult { + Err((errors::ConnectorError::WebhooksNotImplemented).into()) + } + + fn get_webhook_resource_object( + &self, + _request: &api::IncomingWebhookRequestDetails<'_>, + ) -> CustomResult, errors::ConnectorError> { + Err((errors::ConnectorError::WebhooksNotImplemented).into()) + } +} diff --git a/crates/router/src/connector/plaid/transformers.rs b/crates/router/src/connector/plaid/transformers.rs new file mode 100644 index 000000000000..545ea271c434 --- /dev/null +++ b/crates/router/src/connector/plaid/transformers.rs @@ -0,0 +1,389 @@ +use common_enums::Currency; +use common_utils::types::FloatMajorUnit; +use error_stack::ResultExt; +use masking::{PeekInterface, Secret}; +use serde::{Deserialize, Serialize}; + +use crate::{ + connector::utils::is_payment_failure, + core::errors, + types::{self, api, domain, storage::enums}, +}; + +pub struct PlaidRouterData { + pub amount: FloatMajorUnit, + pub router_data: T, +} + +impl From<(FloatMajorUnit, T)> for PlaidRouterData { + fn from((amount, item): (FloatMajorUnit, T)) -> Self { + Self { + amount, + router_data: item, + } + } +} + +#[derive(Default, Debug, Serialize)] +pub struct PlaidPaymentsRequest { + amount: PlaidAmount, + recipient_id: String, + reference: String, + #[serde(skip_serializing_if = "Option::is_none")] + schedule: Option, + #[serde(skip_serializing_if = "Option::is_none")] + options: Option, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct PlaidAmount { + currency: Currency, + value: FloatMajorUnit, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct PlaidSchedule { + interval: String, + interval_execution_day: String, + start_date: String, + end_date: Option, + adjusted_start_date: Option, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct PlaidOptions { + request_refund_details: bool, + iban: Option>, + bacs: Option, + scheme: String, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct PlaidBacs { + account: Secret, + sort_code: Secret, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub struct PlaidLinkTokenRequest { + client_name: String, + country_codes: Vec, + language: String, + products: Vec, + user: User, + payment_initiation: PlaidPaymentInitiation, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct User { + pub client_user_id: String, +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct PlaidPaymentInitiation { + payment_id: String, +} + +impl TryFrom<&PlaidRouterData<&types::PaymentsAuthorizeRouterData>> for PlaidPaymentsRequest { + type Error = error_stack::Report; + fn try_from( + item: &PlaidRouterData<&types::PaymentsAuthorizeRouterData>, + ) -> Result { + match item.router_data.request.payment_method_data.clone() { + domain::PaymentMethodData::OpenBanking(ref data) => match data { + domain::OpenBankingData::OpenBankingPIS { .. } => { + let amount = item.amount; + let currency = item.router_data.request.currency; + let payment_id = item.router_data.payment_id.clone(); + let id_len = payment_id.len(); + let reference = if id_len > 18 { + payment_id.get(id_len - 18..id_len).map(|id| id.to_string()) + } else { + Some(payment_id) + } + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "payment_id", + })?; + let recipient_val = item + .router_data + .connector_meta_data + .as_ref() + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "connector_customer", + })? + .peek() + .clone(); + + let recipient_type = + serde_json::from_value::(recipient_val) + .change_context(errors::ConnectorError::ParsingFailed)?; + let recipient_id = match recipient_type { + types::MerchantRecipientData::ConnectorRecipientId(id) => { + Ok(id.peek().to_string()) + } + _ => Err(errors::ConnectorError::MissingRequiredField { + field_name: "ConnectorRecipientId", + }), + }?; + + Ok(Self { + amount: PlaidAmount { + currency, + value: amount, + }, + reference, + recipient_id, + schedule: None, + options: None, + }) + } + }, + _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } + } +} + +impl TryFrom<&types::PaymentsSyncRouterData> for PlaidSyncRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsSyncRouterData) -> Result { + match item.request.connector_transaction_id { + types::ResponseId::ConnectorTransactionId(ref id) => Ok(Self { + payment_id: id.clone(), + }), + _ => Err((errors::ConnectorError::MissingConnectorTransactionID).into()), + } + } +} + +impl TryFrom<&types::PaymentsPostProcessingRouterData> for PlaidLinkTokenRequest { + type Error = error_stack::Report; + fn try_from(item: &types::PaymentsPostProcessingRouterData) -> Result { + match item.request.payment_method_data.clone() { + domain::PaymentMethodData::OpenBanking(ref data) => match data { + domain::OpenBankingData::OpenBankingPIS { .. } => Ok(Self { + client_name: "Hyperswitch".to_string(), + country_codes: item + .request + .country + .map(|code| vec![code.to_string()]) + .ok_or(errors::ConnectorError::MissingRequiredField { + field_name: "billing.address.country", + })?, + language: "en".to_string(), + products: vec!["payment_initiation".to_string()], + user: User { + client_user_id: item + .request + .customer_id + .clone() + .map(|id| id.get_string_repr().to_string()) + .unwrap_or("default cust".to_string()), + }, + payment_initiation: PlaidPaymentInitiation { + payment_id: item + .request + .connector_transaction_id + .clone() + .ok_or(errors::ConnectorError::MissingConnectorTransactionID)?, + }, + }), + }, + _ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()), + } + } +} + +pub struct PlaidAuthType { + pub client_id: Secret, + pub secret: Secret, +} + +impl TryFrom<&types::ConnectorAuthType> for PlaidAuthType { + type Error = error_stack::Report; + fn try_from(auth_type: &types::ConnectorAuthType) -> Result { + match auth_type { + types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self { + client_id: api_key.to_owned(), + secret: key1.to_owned(), + }), + _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()), + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +#[derive(strum::Display)] +pub enum PlaidPaymentStatus { + PaymentStatusInputNeeded, + PaymentStatusInitiated, + PaymentStatusInsufficientFunds, + PaymentStatusFailed, + PaymentStatusBlocked, + PaymentStatusCancelled, + PaymentStatusExecuted, + PaymentStatusSettled, + PaymentStatusEstablished, + PaymentStatusRejected, + PaymentStatusAuthorising, +} + +impl From for enums::AttemptStatus { + fn from(item: PlaidPaymentStatus) -> Self { + match item { + PlaidPaymentStatus::PaymentStatusAuthorising => Self::Authorizing, + PlaidPaymentStatus::PaymentStatusBlocked => Self::AuthorizationFailed, + PlaidPaymentStatus::PaymentStatusCancelled => Self::Voided, + PlaidPaymentStatus::PaymentStatusEstablished => Self::Authorized, + PlaidPaymentStatus::PaymentStatusExecuted => Self::Authorized, + PlaidPaymentStatus::PaymentStatusFailed => Self::Failure, + PlaidPaymentStatus::PaymentStatusInitiated => Self::AuthenticationPending, + PlaidPaymentStatus::PaymentStatusInputNeeded => Self::AuthenticationPending, + PlaidPaymentStatus::PaymentStatusInsufficientFunds => Self::AuthorizationFailed, + PlaidPaymentStatus::PaymentStatusRejected => Self::AuthorizationFailed, + PlaidPaymentStatus::PaymentStatusSettled => Self::Charged, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct PlaidPaymentsResponse { + status: PlaidPaymentStatus, + payment_id: String, +} + +impl + TryFrom> + for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData, + ) -> Result { + let status = enums::AttemptStatus::from(item.response.status.clone()); + Ok(Self { + status, + response: if is_payment_failure(status) { + Err(types::ErrorResponse { + // populating status everywhere as plaid only sends back a status + code: item.response.status.clone().to_string(), + message: item.response.status.clone().to_string(), + reason: Some(item.response.status.to_string()), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: Some(item.response.payment_id), + }) + } else { + Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + item.response.payment_id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: Some(item.response.payment_id), + incremental_authorization_allowed: None, + charge_id: None, + }) + }, + ..item.data + }) + } +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct PlaidLinkTokenResponse { + link_token: String, +} + +impl + TryFrom> + for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData, + ) -> Result { + let session_token = Some(api::OpenBankingSessionToken { + open_banking_session_token: item.response.link_token, + }); + + Ok(Self { + status: enums::AttemptStatus::AuthenticationPending, + response: Ok(types::PaymentsResponseData::PostProcessingResponse { session_token }), + ..item.data + }) + } +} + +#[derive(Default, Debug, Serialize, Deserialize)] +pub struct PlaidSyncRequest { + payment_id: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct PlaidSyncResponse { + payment_id: String, + amount: PlaidAmount, + status: PlaidPaymentStatus, + recipient_id: String, + reference: String, + last_status_update: String, + adjusted_reference: Option, + schedule: Option, + iban: Option>, + bacs: Option, + scheme: Option, + adjusted_scheme: Option, + request_id: String, +} + +impl TryFrom> + for types::RouterData +{ + type Error = error_stack::Report; + fn try_from( + item: types::ResponseRouterData, + ) -> Result { + let status = enums::AttemptStatus::from(item.response.status.clone()); + Ok(Self { + status, + response: if is_payment_failure(status) { + Err(types::ErrorResponse { + // populating status everywhere as plaid only sends back a status + code: item.response.status.clone().to_string(), + message: item.response.status.clone().to_string(), + reason: Some(item.response.status.to_string()), + status_code: item.http_code, + attempt_status: None, + connector_transaction_id: Some(item.response.payment_id), + }) + } else { + Ok(types::PaymentsResponseData::TransactionResponse { + resource_id: types::ResponseId::ConnectorTransactionId( + item.response.payment_id.clone(), + ), + redirection_data: None, + mandate_reference: None, + connector_metadata: None, + network_txn_id: None, + connector_response_reference_id: Some(item.response.payment_id), + incremental_authorization_allowed: None, + charge_id: None, + }) + }, + ..item.data + }) + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub struct PlaidErrorResponse { + pub display_message: Option, + pub error_code: Option, + pub error_message: String, + pub error_type: Option, +} diff --git a/crates/router/src/core/admin.rs b/crates/router/src/core/admin.rs index a851be95d5ed..bfefe8939778 100644 --- a/crates/router/src/core/admin.rs +++ b/crates/router/src/core/admin.rs @@ -1256,6 +1256,8 @@ pub async fn create_payment_connector( expected_format: "auth_type and api_key".to_string(), })?; + validate_auth_and_metadata_type(req.connector_name, &auth, &req.metadata)?; + let merchant_recipient_data = if let Some(data) = &req.additional_merchant_data { Some( process_open_banking_connectors( @@ -1280,8 +1282,6 @@ pub async fn create_payment_connector( .change_context(errors::ApiErrorResponse::InternalServerError) .attach_printable("Failed to get MerchantRecipientData")?; - validate_auth_and_metadata_type(req.connector_name, &auth, &req.metadata)?; - let frm_configs = get_frm_config_as_secret(req.frm_configs); // The purpose of this merchant account update is just to update the diff --git a/crates/router/src/core/payments/connector_integration_v2_impls.rs b/crates/router/src/core/payments/connector_integration_v2_impls.rs index 5220b3699b27..277326889d33 100644 --- a/crates/router/src/core/payments/connector_integration_v2_impls.rs +++ b/crates/router/src/core/payments/connector_integration_v2_impls.rs @@ -699,7 +699,8 @@ default_imp_for_new_connector_integration_payment!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_refund { @@ -784,7 +785,8 @@ default_imp_for_new_connector_integration_refund!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_connector_access_token { @@ -864,7 +866,8 @@ default_imp_for_new_connector_integration_connector_access_token!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_accept_dispute { @@ -966,7 +969,8 @@ default_imp_for_new_connector_integration_accept_dispute!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_defend_dispute { @@ -1050,7 +1054,8 @@ default_imp_for_new_connector_integration_defend_dispute!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); default_imp_for_new_connector_integration_submit_evidence!( connector::Aci, @@ -1118,7 +1123,8 @@ default_imp_for_new_connector_integration_submit_evidence!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_file_upload { @@ -1213,7 +1219,8 @@ default_imp_for_new_connector_integration_file_upload!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_payouts { @@ -1290,7 +1297,8 @@ default_imp_for_new_connector_integration_payouts!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1377,7 +1385,8 @@ default_imp_for_new_connector_integration_payouts_create!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1464,7 +1473,8 @@ default_imp_for_new_connector_integration_payouts_eligibility!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1551,7 +1561,8 @@ default_imp_for_new_connector_integration_payouts_fulfill!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1638,7 +1649,8 @@ default_imp_for_new_connector_integration_payouts_cancel!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1725,7 +1737,8 @@ default_imp_for_new_connector_integration_payouts_quote!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1812,7 +1825,8 @@ default_imp_for_new_connector_integration_payouts_recipient!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1899,7 +1913,8 @@ default_imp_for_new_connector_integration_payouts_sync!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "payouts")] @@ -1986,7 +2001,8 @@ default_imp_for_new_connector_integration_payouts_recipient_account!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_webhook_source_verification { @@ -2071,7 +2087,8 @@ default_imp_for_new_connector_integration_webhook_source_verification!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_frm { @@ -2148,7 +2165,8 @@ default_imp_for_new_connector_integration_frm!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "frm")] @@ -2235,7 +2253,8 @@ default_imp_for_new_connector_integration_frm_sale!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "frm")] @@ -2322,7 +2341,8 @@ default_imp_for_new_connector_integration_frm_checkout!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "frm")] @@ -2409,7 +2429,8 @@ default_imp_for_new_connector_integration_frm_transaction!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "frm")] @@ -2496,7 +2517,8 @@ default_imp_for_new_connector_integration_frm_fulfillment!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); #[cfg(feature = "frm")] @@ -2583,7 +2605,8 @@ default_imp_for_new_connector_integration_frm_record_return!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_revoking_mandates { @@ -2667,7 +2690,8 @@ default_imp_for_new_connector_integration_revoking_mandates!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); macro_rules! default_imp_for_new_connector_integration_connector_authentication { @@ -2779,5 +2803,6 @@ default_imp_for_new_connector_integration_connector_authentication!( connector::Worldline, connector::Worldpay, connector::Zen, - connector::Zsl + connector::Zsl, + connector::Plaid ); diff --git a/crates/router/src/core/payments/flows.rs b/crates/router/src/core/payments/flows.rs index 57ad68057ff3..a53888414a83 100644 --- a/crates/router/src/core/payments/flows.rs +++ b/crates/router/src/core/payments/flows.rs @@ -221,6 +221,7 @@ default_imp_for_complete_authorize!( connector::Payone, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Rapyd, connector::Razorpay, connector::Riskified, @@ -311,6 +312,7 @@ default_imp_for_webhook_source_verification!( connector::Payone, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -406,6 +408,7 @@ default_imp_for_create_customer!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -489,6 +492,7 @@ default_imp_for_connector_redirect_response!( connector::Payone, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -565,6 +569,7 @@ default_imp_for_connector_request_id!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -662,6 +667,7 @@ default_imp_for_accept_dispute!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -778,6 +784,7 @@ default_imp_for_file_upload!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -872,6 +879,7 @@ default_imp_for_submit_evidence!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -966,6 +974,7 @@ default_imp_for_defend_dispute!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1070,6 +1079,7 @@ default_imp_for_pre_processing_steps!( connector::Payone, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1222,6 +1232,7 @@ default_imp_for_payouts!( connector::Payme, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1314,6 +1325,7 @@ default_imp_for_payouts_create!( connector::Payone, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1408,6 +1420,7 @@ default_imp_for_payouts_retrieve!( connector::Payone, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1506,6 +1519,7 @@ default_imp_for_payouts_eligibility!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1596,6 +1610,7 @@ default_imp_for_payouts_fulfill!( connector::Payme, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1689,6 +1704,7 @@ default_imp_for_payouts_cancel!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1783,6 +1799,7 @@ default_imp_for_payouts_quote!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1878,6 +1895,7 @@ default_imp_for_payouts_recipient!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -1976,6 +1994,7 @@ default_imp_for_payouts_recipient_account!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2071,6 +2090,7 @@ default_imp_for_approve!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2167,6 +2187,7 @@ default_imp_for_reject!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2247,6 +2268,7 @@ default_imp_for_fraud_check!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2343,6 +2365,7 @@ default_imp_for_frm_sale!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2439,6 +2462,7 @@ default_imp_for_frm_checkout!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2535,6 +2559,7 @@ default_imp_for_frm_transaction!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2631,6 +2656,7 @@ default_imp_for_frm_fulfillment!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2727,6 +2753,7 @@ default_imp_for_frm_record_return!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2820,6 +2847,7 @@ default_imp_for_incremental_authorization!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -2912,6 +2940,7 @@ default_imp_for_revoking_mandates!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -3065,6 +3094,7 @@ default_imp_for_connector_authentication!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, @@ -3156,6 +3186,7 @@ default_imp_for_authorize_session_token!( connector::Paypal, connector::Payu, connector::Placetopay, + connector::Plaid, connector::Powertranz, connector::Prophetpay, connector::Rapyd, diff --git a/crates/router/src/core/pm_auth.rs b/crates/router/src/core/pm_auth.rs index 32e5ef308cf8..d6f5ffd9fb9e 100644 --- a/crates/router/src/core/pm_auth.rs +++ b/crates/router/src/core/pm_auth.rs @@ -210,7 +210,6 @@ impl ForeignTryFrom<&types::ConnectorAuthType> for PlaidAuthType { Ok::(Self { client_id: api_key.to_owned(), secret: key1.to_owned(), - merchant_data: None, }) } _ => Err(errors::ConnectorError::FailedToObtainAuthType), diff --git a/crates/router/src/types/api.rs b/crates/router/src/types/api.rs index 92da4ff1960f..f9d27093336d 100644 --- a/crates/router/src/types/api.rs +++ b/crates/router/src/types/api.rs @@ -500,8 +500,10 @@ impl ConnectorData { enums::Connector::Volt => Ok(ConnectorEnum::Old(Box::new(&connector::Volt))), enums::Connector::Zen => Ok(ConnectorEnum::Old(Box::new(&connector::Zen))), enums::Connector::Zsl => Ok(ConnectorEnum::Old(Box::new(&connector::Zsl))), + enums::Connector::Plaid => { + Ok(ConnectorEnum::Old(Box::new(connector::Plaid::new()))) + } enums::Connector::Signifyd - | enums::Connector::Plaid | enums::Connector::Riskified | enums::Connector::Gpayments | enums::Connector::Threedsecureio => { diff --git a/crates/router/src/types/domain/payments.rs b/crates/router/src/types/domain/payments.rs index a3ed6b886a1b..1d3275985f6e 100644 --- a/crates/router/src/types/domain/payments.rs +++ b/crates/router/src/types/domain/payments.rs @@ -4,8 +4,8 @@ pub use hyperswitch_domain_models::payment_method_data::{ CardToken, CashappQr, CryptoData, GcashRedirection, GiftCardData, GiftCardDetails, GoPayRedirection, GooglePayPaymentMethodInfo, GooglePayRedirectData, GooglePayThirdPartySdkData, GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData, - KakaoPayRedirection, MbWayRedirection, MifinityData, PayLaterData, PaymentMethodData, - RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, SwishQrData, - TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData, - WeChatPayQr, + KakaoPayRedirection, MbWayRedirection, MifinityData, OpenBankingData, PayLaterData, + PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, + SwishQrData, TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, + WalletData, WeChatPayQr, }; diff --git a/crates/router/src/types/transformers.rs b/crates/router/src/types/transformers.rs index d32f62ed0be0..3cad4f16e818 100644 --- a/crates/router/src/types/transformers.rs +++ b/crates/router/src/types/transformers.rs @@ -274,11 +274,7 @@ impl ForeignTryFrom for common_enums::RoutableConnectors { api_enums::Connector::Paypal => Self::Paypal, api_enums::Connector::Payu => Self::Payu, api_models::enums::Connector::Placetopay => Self::Placetopay, - api_enums::Connector::Plaid => { - Err(common_utils::errors::ValidationError::InvalidValue { - message: "plaid is not a routable connector".to_string(), - })? - } + api_enums::Connector::Plaid => Self::Plaid, api_enums::Connector::Powertranz => Self::Powertranz, api_enums::Connector::Prophetpay => Self::Prophetpay, api_enums::Connector::Rapyd => Self::Rapyd, diff --git a/crates/router/tests/connectors/main.rs b/crates/router/tests/connectors/main.rs index 5fb2f5f004e5..2b8d011cb79c 100644 --- a/crates/router/tests/connectors/main.rs +++ b/crates/router/tests/connectors/main.rs @@ -57,6 +57,7 @@ mod payone; mod paypal; mod payu; mod placetopay; +mod plaid; mod powertranz; #[cfg(feature = "dummy_connector")] mod prophetpay; diff --git a/crates/router/tests/connectors/plaid.rs b/crates/router/tests/connectors/plaid.rs new file mode 100644 index 000000000000..bcce36347e2e --- /dev/null +++ b/crates/router/tests/connectors/plaid.rs @@ -0,0 +1,421 @@ +use masking::Secret; +use router::types::{self, api, domain, storage::enums}; +use test_utils::connector_auth; + +use crate::utils::{self, Connector, ConnectorActions}; + +#[derive(Clone, Copy)] +struct PlaidTest; +impl ConnectorActions for PlaidTest {} + +static CONNECTOR: PlaidTest = PlaidTest {}; + +impl Connector for PlaidTest { + fn get_data(&self) -> api::ConnectorData { + use router::connector::Plaid; + utils::construct_connector_data_old( + Box::new(Plaid::new()), + types::Connector::Plaid, + api::GetToken::Connector, + None, + ) + } + + fn get_auth_token(&self) -> types::ConnectorAuthType { + utils::to_connector_auth_type( + connector_auth::ConnectorAuthentication::new() + .plaid + .expect("Missing connector authentication configuration") + .into(), + ) + } + + fn get_name(&self) -> String { + "plaid".to_string() + } +} + +fn get_default_payment_info() -> Option { + None +} + +fn payment_method_details() -> Option { + None +} + +// Cards Positive Tests +// Creates a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_only_authorize_payment() { + let response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized); +} + +// Captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment(payment_method_details(), None, get_default_payment_info()) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Partially captures a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_capture_authorized_payment() { + let response = CONNECTOR + .authorize_and_capture_payment( + payment_method_details(), + Some(types::PaymentsCaptureData { + amount_to_capture: 50, + ..utils::PaymentCaptureType::default().0 + }), + get_default_payment_info(), + ) + .await + .expect("Capture payment response"); + assert_eq!(response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_authorized_payment() { + let authorize_response = CONNECTOR + .authorize_payment(payment_method_details(), get_default_payment_info()) + .await + .expect("Authorize payment response"); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Authorized, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("PSync response"); + assert_eq!(response.status, enums::AttemptStatus::Authorized,); +} + +// Voids a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_void_authorized_payment() { + let response = CONNECTOR + .authorize_and_void_payment( + payment_method_details(), + Some(types::PaymentsCancelData { + connector_transaction_id: String::from(""), + cancellation_reason: Some("requested_by_customer".to_string()), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .expect("Void payment response"); + assert_eq!(response.status, enums::AttemptStatus::Voided); +} + +// Refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_manually_captured_payment() { + let response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Synchronizes a refund using the manual capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_manually_captured_refund() { + let refund_response = CONNECTOR + .capture_payment_and_refund( + payment_method_details(), + None, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_make_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); +} + +// Synchronizes a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_auto_captured_payment() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let response = CONNECTOR + .psync_retry_till_status_matches( + enums::AttemptStatus::Charged, + Some(types::PaymentsSyncData { + connector_transaction_id: types::ResponseId::ConnectorTransactionId( + txn_id.unwrap(), + ), + capture_method: Some(enums::CaptureMethod::Automatic), + ..Default::default() + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!(response.status, enums::AttemptStatus::Charged,); +} + +// Refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_auto_captured_payment() { + let response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Partially refunds a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_partially_refund_succeeded_payment() { + let refund_response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + refund_response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Creates multiple refunds against a payment using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_refund_succeeded_payment_multiple_times() { + CONNECTOR + .make_payment_and_multiple_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 50, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await; +} + +// Synchronizes a refund using the automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_sync_refund() { + let refund_response = CONNECTOR + .make_payment_and_refund(payment_method_details(), None, get_default_payment_info()) + .await + .unwrap(); + let response = CONNECTOR + .rsync_retry_till_status_matches( + enums::RefundStatus::Success, + refund_response.response.unwrap().connector_refund_id, + None, + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap().refund_status, + enums::RefundStatus::Success, + ); +} + +// Cards Negative scenarios +// Creates a payment with incorrect CVC. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_cvc() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_cvc: Secret::new("12345".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Invalid card cvc.".to_string(), + ); +} + +// Creates a payment with incorrect expiry month. +#[actix_web::test] +async fn should_fail_payment_for_invalid_exp_month() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_month: Secret::new("20".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration month is invalid.".to_string(), + ); +} + +// Creates a payment with incorrect expiry year. +#[actix_web::test] +async fn should_fail_payment_for_incorrect_expiry_year() { + let response = CONNECTOR + .make_payment( + Some(types::PaymentsAuthorizeData { + payment_method_data: domain::PaymentMethodData::Card(domain::Card { + card_exp_year: Secret::new("2000".to_string()), + ..utils::CCardType::default().0 + }), + ..utils::PaymentAuthorizeType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Your card's expiration year is invalid.".to_string(), + ); +} + +// Voids a payment using automatic capture flow (Non 3DS). +#[actix_web::test] +async fn should_fail_void_payment_for_auto_capture() { + let authorize_response = CONNECTOR + .make_payment(payment_method_details(), get_default_payment_info()) + .await + .unwrap(); + assert_eq!(authorize_response.status, enums::AttemptStatus::Charged); + let txn_id = utils::get_connector_transaction_id(authorize_response.response); + assert_ne!(txn_id, None, "Empty connector transaction id"); + let void_response = CONNECTOR + .void_payment(txn_id.unwrap(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + void_response.response.unwrap_err().message, + "You cannot cancel this PaymentIntent because it has a status of succeeded." + ); +} + +// Captures a payment using invalid connector payment id. +#[actix_web::test] +async fn should_fail_capture_for_invalid_payment() { + let capture_response = CONNECTOR + .capture_payment("123456789".to_string(), None, get_default_payment_info()) + .await + .unwrap(); + assert_eq!( + capture_response.response.unwrap_err().message, + String::from("No such payment_intent: '123456789'") + ); +} + +// Refunds a payment with refund amount higher than payment amount. +#[actix_web::test] +async fn should_fail_for_refund_amount_higher_than_payment_amount() { + let response = CONNECTOR + .make_payment_and_refund( + payment_method_details(), + Some(types::RefundsData { + refund_amount: 150, + ..utils::PaymentRefundType::default().0 + }), + get_default_payment_info(), + ) + .await + .unwrap(); + assert_eq!( + response.response.unwrap_err().message, + "Refund amount (₹1.50) is greater than charge amount (₹1.00)", + ); +} + +// Connector dependent test cases goes here + +// [#478]: add unit tests for non 3DS, wallets & webhooks in connector tests diff --git a/crates/router/tests/connectors/sample_auth.toml b/crates/router/tests/connectors/sample_auth.toml index 443034b2736a..a39b799b820b 100644 --- a/crates/router/tests/connectors/sample_auth.toml +++ b/crates/router/tests/connectors/sample_auth.toml @@ -195,6 +195,10 @@ api_key= "Login" key1= "Trankey" +[plaid] +api_key="Client Id" +key1= "Secret" + [threedsecureio] api_key="API Key" diff --git a/crates/test_utils/src/connector_auth.rs b/crates/test_utils/src/connector_auth.rs index 56058d787498..b190c009818c 100644 --- a/crates/test_utils/src/connector_auth.rs +++ b/crates/test_utils/src/connector_auth.rs @@ -60,6 +60,7 @@ pub struct ConnectorAuthentication { pub paypal: Option, pub payu: Option, pub placetopay: Option, + pub plaid: Option, pub powertranz: Option, pub prophetpay: Option, pub rapyd: Option, diff --git a/loadtest/config/development.toml b/loadtest/config/development.toml index 2daeecf2854a..1bcd47abf8fd 100644 --- a/loadtest/config/development.toml +++ b/loadtest/config/development.toml @@ -127,6 +127,7 @@ payone.base_url = "https://payment.preprod.payone.com/" paypal.base_url = "https://api-m.sandbox.paypal.com/" payu.base_url = "https://secure.snd.payu.com/" placetopay.base_url = "https://test.placetopay.com/rest/gateway" +plaid.base_url = "https://sandbox.plaid.com" powertranz.base_url = "https://staging.ptranz.com/api/" prophetpay.base_url = "https://ccm-thirdparty.cps.golf/" rapyd.base_url = "https://sandboxapi.rapyd.net" @@ -204,6 +205,7 @@ cards = [ "paypal", "payu", "placetopay", + "plaid", "powertranz", "prophetpay", "shift4", diff --git a/scripts/add_connector.sh b/scripts/add_connector.sh index 87822b2d1c62..459034619255 100755 --- a/scripts/add_connector.sh +++ b/scripts/add_connector.sh @@ -6,7 +6,7 @@ function find_prev_connector() { git checkout $self cp $self $self.tmp # Add new connector to existing list and sort it - connectors=(aci adyen adyenplatform airwallex applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless gpayments helcim iatapay itaubank klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode payeezy payme payone paypal payu placetopay powertranz prophetpay rapyd razorpay shift4 square stax stripe threedsecureio trustpay tsys volt wise worldline worldpay zsl "$1") + connectors=(aci adyen adyenplatform airwallex applepay authorizedotnet bambora bamboraapac bankofamerica billwerk bitpay bluesnap boku braintree cashtocode checkout coinbase cryptopay cybersource datatrans dlocal dummyconnector ebanx fiserv forte globalpay globepay gocardless gpayments helcim iatapay itaubank klarna mifinity mollie multisafepay netcetera nexinets noon nuvei opayo opennode payeezy payme payone paypal payu placetopay plaid powertranz prophetpay rapyd razorpay shift4 square stax stripe threedsecureio trustpay tsys volt wise worldline worldpay zsl "$1") IFS=$'\n' sorted=($(sort <<<"${connectors[*]}")); unset IFS res=`echo ${sorted[@]}` sed -i'' -e "s/^ connectors=.*/ connectors=($res \"\$1\")/" $self.tmp From a8e2f3ebc9da60ef41459b080d8856d3cadf8c41 Mon Sep 17 00:00:00 2001 From: Shankar Singh C <83439957+ShankarSinghC@users.noreply.github.com> Date: Fri, 19 Jul 2024 14:01:11 +0530 Subject: [PATCH 24/26] refactor(connector): make the `original_authorized_amount` optional for MITs with `connector_mandate_details` (#5311) --- .../src/connector/cybersource/transformers.rs | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/crates/router/src/connector/cybersource/transformers.rs b/crates/router/src/connector/cybersource/transformers.rs index 2f286964e3e6..86960b43ca40 100644 --- a/crates/router/src/connector/cybersource/transformers.rs +++ b/crates/router/src/connector/cybersource/transformers.rs @@ -589,12 +589,30 @@ impl Some(payments::MandateReferenceId::ConnectorMandateId(_)) => { let original_amount = item .router_data - .get_recurring_mandate_payment_data()? - .get_original_payment_amount()?; + .recurring_mandate_payment_data + .as_ref() + .and_then(|recurring_mandate_payment_data| { + recurring_mandate_payment_data.original_payment_authorized_amount + }); + let original_currency = item .router_data - .get_recurring_mandate_payment_data()? - .get_original_payment_currency()?; + .recurring_mandate_payment_data + .as_ref() + .and_then(|recurring_mandate_payment_data| { + recurring_mandate_payment_data.original_payment_authorized_currency + }); + + let original_authorized_amount = match original_amount.zip(original_currency) { + Some((original_amount, original_currency)) => { + Some(utils::get_amount_as_string( + &api::CurrencyUnit::Base, + original_amount, + original_currency, + )?) + } + None => None, + }; ( None, None, @@ -602,11 +620,7 @@ impl initiator: None, merchant_intitiated_transaction: Some(MerchantInitiatedTransaction { reason: None, - original_authorized_amount: Some(utils::get_amount_as_string( - &api::CurrencyUnit::Base, - original_amount, - original_currency, - )?), + original_authorized_amount, previous_transaction_id: None, }), }), @@ -618,7 +632,8 @@ impl .map(|network| network.to_lowercase()) .as_deref() { - Some("discover") => { + //This is to make original_authorized_amount mandatory for discover card networks in NetworkMandateId flow + Some("004") => { let original_amount = Some( item.router_data .get_recurring_mandate_payment_data()? @@ -653,12 +668,11 @@ impl (original_amount, original_currency) } }; - - let original_authorized_amount = match (original_amount, original_currency) { - (Some(original_amount), Some(original_currency)) => Some( + let original_authorized_amount = match original_amount.zip(original_currency) { + Some((original_amount, original_currency)) => Some( utils::to_currency_base_unit(original_amount, original_currency)?, ), - _ => None, + None => None, }; commerce_indicator = "recurring".to_string(); ( From 476aed5036eb41671c0736887d02dcac7f3573c6 Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 19 Jul 2024 09:06:52 +0000 Subject: [PATCH 25/26] chore(version): 2024.07.19.1 --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81e590a7726d..b22611a9b53a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,22 @@ All notable changes to HyperSwitch will be documented here. - - - +## 2024.07.19.1 + +### Features + +- **connector:** Plaid connector Integration ([#3952](https://github.com/juspay/hyperswitch/pull/3952)) ([`eb01680`](https://github.com/juspay/hyperswitch/commit/eb01680284fea4d61ef95418878d49104885352e)) +- Encryption service integration to support batch encryption and decryption ([#5164](https://github.com/juspay/hyperswitch/pull/5164)) ([`33298b3`](https://github.com/juspay/hyperswitch/commit/33298b38081c46fe4ee38f8ad6ddffd2b98a1d5c)) + +### Refactors + +- **connector:** Make the `original_authorized_amount` optional for MITs with `connector_mandate_details` ([#5311](https://github.com/juspay/hyperswitch/pull/5311)) ([`a8e2f3e`](https://github.com/juspay/hyperswitch/commit/a8e2f3ebc9da60ef41459b080d8856d3cadf8c41)) +- **core:** Change primary key of refund table ([#5367](https://github.com/juspay/hyperswitch/pull/5367)) ([`c698921`](https://github.com/juspay/hyperswitch/commit/c698921c417da4f6f74887224818ccb5d92b9fc3)) + +**Full Changelog:** [`2024.07.19.0...2024.07.19.1`](https://github.com/juspay/hyperswitch/compare/2024.07.19.0...2024.07.19.1) + +- - - + ## 2024.07.19.0 ### Features From b51c8e1d12c2f0012b8210a6c25c989f9dd89c3b Mon Sep 17 00:00:00 2001 From: Prajjwal Kumar Date: Fri, 19 Jul 2024 18:26:40 +0530 Subject: [PATCH 26/26] refactor(core): change primary keys in user, user_roles and roles tables (#5374) --- crates/diesel_models/src/schema.rs | 6 +++--- crates/diesel_models/src/schema_v2.rs | 6 +++--- .../down.sql | 5 +++++ .../up.sql | 12 ++++++++++++ .../down.sql | 5 +++++ .../up.sql | 12 ++++++++++++ .../down.sql | 5 +++++ .../up.sql | 12 ++++++++++++ 8 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 migrations/2024-07-19-095541_change_primary_key_for_users/down.sql create mode 100644 migrations/2024-07-19-095541_change_primary_key_for_users/up.sql create mode 100644 migrations/2024-07-19-100016_change_primary_key_for_user_roles/down.sql create mode 100644 migrations/2024-07-19-100016_change_primary_key_for_user_roles/up.sql create mode 100644 migrations/2024-07-19-100936_change_primary_key_for_roles/down.sql create mode 100644 migrations/2024-07-19-100936_change_primary_key_for_roles/up.sql diff --git a/crates/diesel_models/src/schema.rs b/crates/diesel_models/src/schema.rs index 4b5f4a8ad49f..b8c5344cbbba 100644 --- a/crates/diesel_models/src/schema.rs +++ b/crates/diesel_models/src/schema.rs @@ -1168,7 +1168,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - roles (id) { + roles (role_id) { id -> Int4, #[max_length = 64] role_name -> Varchar, @@ -1251,7 +1251,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - user_roles (id) { + user_roles (user_id, merchant_id) { id -> Int4, #[max_length = 64] user_id -> Varchar, @@ -1275,7 +1275,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - users (id) { + users (user_id) { id -> Int4, #[max_length = 64] user_id -> Varchar, diff --git a/crates/diesel_models/src/schema_v2.rs b/crates/diesel_models/src/schema_v2.rs index a2e35a6d6a98..c831bbfa7093 100644 --- a/crates/diesel_models/src/schema_v2.rs +++ b/crates/diesel_models/src/schema_v2.rs @@ -1166,7 +1166,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - roles (id) { + roles (role_id) { id -> Int4, #[max_length = 64] role_name -> Varchar, @@ -1249,7 +1249,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - user_roles (id) { + user_roles (user_id, merchant_id) { id -> Int4, #[max_length = 64] user_id -> Varchar, @@ -1273,7 +1273,7 @@ diesel::table! { use diesel::sql_types::*; use crate::enums::diesel_exports::*; - users (id) { + users (user_id) { id -> Int4, #[max_length = 64] user_id -> Varchar, diff --git a/migrations/2024-07-19-095541_change_primary_key_for_users/down.sql b/migrations/2024-07-19-095541_change_primary_key_for_users/down.sql new file mode 100644 index 000000000000..6ba57be2af41 --- /dev/null +++ b/migrations/2024-07-19-095541_change_primary_key_for_users/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE users DROP CONSTRAINT users_pkey; + +ALTER TABLE users +ADD PRIMARY KEY (id); diff --git a/migrations/2024-07-19-095541_change_primary_key_for_users/up.sql b/migrations/2024-07-19-095541_change_primary_key_for_users/up.sql new file mode 100644 index 000000000000..3d53115c1722 --- /dev/null +++ b/migrations/2024-07-19-095541_change_primary_key_for_users/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here +-- The below query will lock the users table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query should be run after the new version of application is deployed +ALTER TABLE users DROP CONSTRAINT users_pkey; + +-- Use the `user_id` columns as primary key +-- These are already unique, not null column +-- So this query should not fail for not null or duplicate value reasons +ALTER TABLE users +ADD PRIMARY KEY (user_id); diff --git a/migrations/2024-07-19-100016_change_primary_key_for_user_roles/down.sql b/migrations/2024-07-19-100016_change_primary_key_for_user_roles/down.sql new file mode 100644 index 000000000000..47b9f679c400 --- /dev/null +++ b/migrations/2024-07-19-100016_change_primary_key_for_user_roles/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE user_roles DROP CONSTRAINT user_roles_pkey; + +ALTER TABLE user_roles +ADD PRIMARY KEY (id); diff --git a/migrations/2024-07-19-100016_change_primary_key_for_user_roles/up.sql b/migrations/2024-07-19-100016_change_primary_key_for_user_roles/up.sql new file mode 100644 index 000000000000..65d9c61fcb83 --- /dev/null +++ b/migrations/2024-07-19-100016_change_primary_key_for_user_roles/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here +-- The below query will lock the user_roles table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query should be run after the new version of application is deployed +ALTER TABLE user_roles DROP CONSTRAINT user_roles_pkey; + +-- Use the `user_id, merchant_id` columns as primary key +-- These are already unique, not null columns +-- So this query should not fail for not null or duplicate value reasons +ALTER TABLE user_roles +ADD PRIMARY KEY (user_id, merchant_id); diff --git a/migrations/2024-07-19-100936_change_primary_key_for_roles/down.sql b/migrations/2024-07-19-100936_change_primary_key_for_roles/down.sql new file mode 100644 index 000000000000..70e4d1674af7 --- /dev/null +++ b/migrations/2024-07-19-100936_change_primary_key_for_roles/down.sql @@ -0,0 +1,5 @@ +-- This file should undo anything in `up.sql` +ALTER TABLE roles DROP CONSTRAINT roles_pkey; + +ALTER TABLE roles +ADD PRIMARY KEY (id); diff --git a/migrations/2024-07-19-100936_change_primary_key_for_roles/up.sql b/migrations/2024-07-19-100936_change_primary_key_for_roles/up.sql new file mode 100644 index 000000000000..8bde2a3b4266 --- /dev/null +++ b/migrations/2024-07-19-100936_change_primary_key_for_roles/up.sql @@ -0,0 +1,12 @@ +-- Your SQL goes here +-- The below query will lock the user_roles table +-- Running this query is not necessary on higher environments +-- as the application will work fine without these queries being run +-- This query should be run after the new version of application is deployed +ALTER TABLE roles DROP CONSTRAINT roles_pkey; + +-- Use the `role_id` column as primary key +-- These are already unique, not null column +-- So this query should not fail for not null or duplicate value reasons +ALTER TABLE roles +ADD PRIMARY KEY (role_id);