Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(router): add endpoint for listing connector features #6612

Merged
merged 53 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
35a6128
add payment method validation
AkshayaFoiger Nov 17, 2024
0e82f43
add /feature_matrix endpoint
AkshayaFoiger Nov 19, 2024
3a508bb
Merge branch 'main' into pm-details-doc
AkshayaFoiger Nov 19, 2024
81082ec
chore: run formatter
hyperswitch-bot[bot] Nov 19, 2024
fc46d91
add support for deutshebank
AkshayaFoiger Nov 20, 2024
9f1e4fe
chore: run formatter
hyperswitch-bot[bot] Nov 20, 2024
05a35be
add country and currency in pm details
AkshayaFoiger Dec 2, 2024
87aae38
refactor(router): add paymentMethod to validate_capture and resolve c…
AkshayaFoiger Dec 5, 2024
169285d
feat(configs): add bambora country and currency
AkshayaFoiger Dec 5, 2024
9ba4b17
feat(cypress): add a test case
AkshayaFoiger Dec 5, 2024
5f63fa1
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 5, 2024
3298399
chore(cypress): run formatter and address lints
hyperswitch-bot[bot] Dec 5, 2024
f66bfb3
chore: run formatter
hyperswitch-bot[bot] Dec 5, 2024
616c7b6
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 6, 2024
2c9663f
fix(router): resolve merge conflict
AkshayaFoiger Dec 6, 2024
f1ffeb8
fix(router): remove unnecessary qualification for enum
AkshayaFoiger Dec 6, 2024
a21d1c0
chore: run formatter
hyperswitch-bot[bot] Dec 6, 2024
d8e001a
feat(router): add ConnectorSpecifications trait
AkshayaFoiger Dec 8, 2024
c598713
refactor(cypress): resolve comment
AkshayaFoiger Dec 9, 2024
f075052
feat(openapi): add request and response struct to openapi
AkshayaFoiger Dec 9, 2024
454110f
refactor(router): change the type of supported_webhook_flows field
AkshayaFoiger Dec 9, 2024
3b500f6
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 9, 2024
ca4e8d5
fix(router): merge conflicts
AkshayaFoiger Dec 9, 2024
bb26e3c
fix(router): fix formatting
AkshayaFoiger Dec 9, 2024
001754d
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 9, 2024
9cf45c7
fix(clippy): remove ref from validation functions
AkshayaFoiger Dec 10, 2024
6ebe4c2
chore: run formatter
hyperswitch-bot[bot] Dec 10, 2024
8562f90
refactor(feature_matrix): rename fields and generate openapi_spec
AkshayaFoiger Dec 10, 2024
0c8421a
refactor(router): remove validate_capture_method from zsl
AkshayaFoiger Dec 10, 2024
7641750
fix(configs): remove CAD from pm_filters.bambora
AkshayaFoiger Dec 10, 2024
0f30c99
chore: fix clippy error
AkshayaFoiger Dec 12, 2024
2482e9e
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 12, 2024
b84ef1b
fix(router): resolve merge conflicts
AkshayaFoiger Dec 12, 2024
27a302c
chore: run formatter
hyperswitch-bot[bot] Dec 12, 2024
9533913
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 13, 2024
c1f050d
chore: fix open_sepc error
AkshayaFoiger Dec 13, 2024
b4394de
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 16, 2024
db53775
fix(cypress): change assertion field name
AkshayaFoiger Dec 16, 2024
e33acec
refactor(router): remove type import
AkshayaFoiger Dec 17, 2024
0e0dde8
refactor(router): merge validate_capture_method() and validate_paymen…
AkshayaFoiger Dec 17, 2024
ab161b0
refactor(feature_matrix): remove payment method wise nesting
AkshayaFoiger Dec 18, 2024
f2d97d9
refactor(router): change the return type of ConnectorSpecifications f…
AkshayaFoiger Dec 19, 2024
ffdb4e5
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 19, 2024
e138851
chore: fix merge conflicts
AkshayaFoiger Dec 19, 2024
fbce038
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 20, 2024
6e0c329
chore: fix formatting error
AkshayaFoiger Dec 20, 2024
71f623b
chore: fix formatiing
AkshayaFoiger Dec 20, 2024
f956a62
chore: fix clippy error
AkshayaFoiger Dec 20, 2024
9fa021b
chore: fix openapi_spec
AkshayaFoiger Dec 20, 2024
07f8a1c
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 20, 2024
11bf497
Update cypress-tests/cypress/support/commands.js
AkshayaFoiger Dec 20, 2024
954fa23
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 23, 2024
9a3116f
Merge branch 'main' into pm-details-doc
AkshayaFoiger Dec 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions crates/api_models/src/feature_matrix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use crate::enums;

#[derive(Default, Debug, Deserialize, Serialize, Clone, ToSchema)]
pub struct FeatureMatrixRequest {
// List of connectors for which the feature matrix is requested
pub connectors: Option<Vec<enums::Connector>>,
AkshayaFoiger marked this conversation as resolved.
Show resolved Hide resolved
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, Serialize)]
pub struct SupportedPaymentMethod {
pub payment_method: enums::PaymentMethodType,
pub availability_status: enums::PaymentMethodStage,
pub supports_mandates: bool,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, ToSchema, Serialize)]
pub struct SupportedPaymentMethodTypes {
pub payment_method_type: enums::PaymentMethod,
pub payment_methods: Vec<SupportedPaymentMethod>,
}

#[cfg(feature = "v1")]
#[derive(Clone, Debug, ToSchema, Serialize)]
pub struct FeatureMatrixResponse {
pub connector: enums::Connector,
pub payment_method_types: Vec<SupportedPaymentMethodTypes>,
}

#[derive(Clone, Debug, serde::Serialize, ToSchema)]
pub struct FeatureMatrixListResponse {
/// The number of connectors included in the list
pub size: usize,
// The list of payments response objects
pub data: Vec<FeatureMatrixResponse>,
}

impl common_utils::events::ApiEventMetric for FeatureMatrixListResponse {}
impl common_utils::events::ApiEventMetric for FeatureMatrixRequest {}
1 change: 1 addition & 0 deletions crates/api_models/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod ephemeral_key;
#[cfg(feature = "errors")]
pub mod errors;
pub mod events;
pub mod feature_matrix;
pub mod files;
pub mod gsm;
pub mod health_check;
Expand Down
27 changes: 27 additions & 0 deletions crates/common_enums/src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1643,6 +1643,33 @@ pub enum PaymentMethod {
MobilePayment,
}

#[derive(
Clone,
Copy,
Debug,
Default,
Eq,
Hash,
PartialEq,
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::VariantNames,
strum::EnumIter,
strum::EnumString,
ToSchema,
)]
#[strum(serialize_all = "snake_case")]
pub enum PaymentMethodStage {
AkshayaFoiger marked this conversation as resolved.
Show resolved Hide resolved
/// Payment Method available in production
#[default]
AkshayaFoiger marked this conversation as resolved.
Show resolved Hide resolved
Live,
/// Payment Method available in sandbox
Beta,
/// Payment Method not available
AkshayaFoiger marked this conversation as resolved.
Show resolved Hide resolved
Upcoming,
}

/// The type of the payment that differentiates between normal and various types of mandate payments. Use 'setup_mandate' in case of zero auth flow.
#[derive(
Clone,
Expand Down
26 changes: 24 additions & 2 deletions crates/hyperswitch_connectors/src/connectors/bambora.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ use hyperswitch_interfaces::{
errors,
events::connector_api_logs::ConnectorEvent,
types::{
self, PaymentsAuthorizeType, PaymentsCaptureType, PaymentsCompleteAuthorizeType,
PaymentsSyncType, PaymentsVoidType, Response,
self, PaymentMethodDetails, PaymentMethodTypeMetadata, PaymentsAuthorizeType,
PaymentsCaptureType, PaymentsCompleteAuthorizeType, PaymentsSyncType, PaymentsVoidType,
Response, SupportedPaymentMethods,
},
webhooks,
};
Expand Down Expand Up @@ -119,6 +120,27 @@ impl ConnectorCommon for Bambora {
)])
}

fn get_supported_payment_methods(&self) -> Option<SupportedPaymentMethods> {
let mut supported_payment_methods = SupportedPaymentMethods::new();
let mut card_payment_method = PaymentMethodTypeMetadata::new();
card_payment_method.insert(
enums::PaymentMethodType::Credit,
PaymentMethodDetails {
availability_status: enums::PaymentMethodStage::Live,
supports_mandates: false,
},
);
card_payment_method.insert(
enums::PaymentMethodType::Debit,
PaymentMethodDetails {
availability_status: enums::PaymentMethodStage::Live,
supports_mandates: false,
},
);
supported_payment_methods.insert(enums::PaymentMethod::Card, card_payment_method);
Some(supported_payment_methods)
}
AkshayaFoiger marked this conversation as resolved.
Show resolved Hide resolved

fn build_error_response(
&self,
res: Response,
Expand Down
19 changes: 18 additions & 1 deletion crates/hyperswitch_connectors/src/connectors/deutschebank.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ use hyperswitch_interfaces::{
configs::Connectors,
errors,
events::connector_api_logs::ConnectorEvent,
types::{self, Response},
types::{
self, PaymentMethodDetails, PaymentMethodTypeMetadata, Response, SupportedPaymentMethods,
},
webhooks,
};
use masking::{ExposeInterface, Mask, Secret};
Expand Down Expand Up @@ -135,6 +137,21 @@ impl ConnectorCommon for Deutschebank {
connectors.deutschebank.base_url.as_ref()
}

fn get_supported_payment_methods(&self) -> Option<SupportedPaymentMethods> {
let mut supported_payment_methods = SupportedPaymentMethods::new();
let mut bank_debit_payment_method = PaymentMethodTypeMetadata::new();
bank_debit_payment_method.insert(
enums::PaymentMethodType::Sepa,
PaymentMethodDetails {
availability_status: enums::PaymentMethodStage::Live,
supports_mandates: true,
},
);
supported_payment_methods
.insert(enums::PaymentMethod::BankDebit, bank_debit_payment_method);
Some(supported_payment_methods)
}

fn get_auth_header(
&self,
auth_type: &ConnectorAuthType,
Expand Down
76 changes: 73 additions & 3 deletions crates/hyperswitch_interfaces/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ pub mod payouts;
pub mod payouts_v2;
pub mod refunds;
pub mod refunds_v2;
use common_enums::enums::{CallConnectorAction, CaptureMethod, PaymentAction, PaymentMethodType};

use common_enums::{
enums::{CallConnectorAction, CaptureMethod, PaymentAction, PaymentMethodType},
PaymentMethod, PaymentMethodStage,
};
use common_utils::{
errors::CustomResult,
request::{Method, Request, RequestContent},
Expand All @@ -42,8 +46,12 @@ use serde_json::json;
pub use self::payouts::*;
pub use self::{payments::*, refunds::*};
use crate::{
configs::Connectors, connector_integration_v2::ConnectorIntegrationV2, consts, errors,
events::connector_api_logs::ConnectorEvent, metrics, types,
configs::Connectors,
connector_integration_v2::ConnectorIntegrationV2,
consts, errors,
events::connector_api_logs::ConnectorEvent,
metrics,
types::{self, SupportedPaymentMethods},
};

/// type BoxedConnectorIntegration
Expand Down Expand Up @@ -257,6 +265,11 @@ pub trait ConnectorCommon {
/// The base URL for interacting with the connector's API.
fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str;

/// Details related to payment method supported by the connector
fn get_supported_payment_methods(&self) -> Option<SupportedPaymentMethods> {
None
}

/// common error response for a connector if it is same in all case
fn build_error_response(
&self,
Expand Down Expand Up @@ -340,6 +353,63 @@ pub trait ConnectorVerifyWebhookSourceV2:

/// trait ConnectorValidation
pub trait ConnectorValidation: ConnectorCommon {
/// fn validate_payment_method
fn validate_payment_method(
AkshayaFoiger marked this conversation as resolved.
Show resolved Hide resolved
&self,
payment_method_type: &Option<PaymentMethodType>,
AkshayaFoiger marked this conversation as resolved.
Show resolved Hide resolved
payment_method: &PaymentMethod,
is_mandate_payment: bool,
test_mode: bool,
) -> CustomResult<(), errors::ConnectorError> {
match self.get_supported_payment_methods() {
Some(supported_payment_methods) => {
// Check if the payment method exists
let payment_method_information = supported_payment_methods
.get(payment_method)
.ok_or_else(|| errors::ConnectorError::NotSupported {
message: payment_method.to_string(),
connector: self.id(),
})?;

match payment_method_type {
Some(pmt) => {
// Check if the payment method type exists
let payment_method_type_information =
payment_method_information.get(pmt).ok_or_else(|| {
errors::ConnectorError::NotSupported {
message: format!("{:?}, {:?}", pmt, payment_method),
connector: self.id(),
}
})?;
// Validate the payment method type based on its availability and mandate support
match (
test_mode,
is_mandate_payment,
payment_method_type_information.availability_status.clone(),
payment_method_type_information.supports_mandates,
) {
// Test mode mandate payment
(true, true, PaymentMethodStage::Live | PaymentMethodStage::Beta, true) |
// Test mode payment
(true, false, PaymentMethodStage::Live | PaymentMethodStage::Beta, _) |
// Live mode mandate payment
(false, true, PaymentMethodStage::Live, true) |
// Live mode payment
(false, false, PaymentMethodStage::Live, _) => Ok(()),
// If none of the cases match, return an unsupported error
_ => Err(errors::ConnectorError::NotSupported {
message: format!("{:?}, {:?}", payment_method_type, payment_method),
connector: self.id(),
}.into()),
}
}
None => Ok(()),
}
}
None => Ok(()),
}
}

/// fn validate_capture_method
fn validate_capture_method(
&self,
Expand Down
19 changes: 19 additions & 0 deletions crates/hyperswitch_interfaces/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//! Types interface
use std::collections::HashMap;

use common_enums::{PaymentMethod, PaymentMethodStage, PaymentMethodType};
use hyperswitch_domain_models::{
router_data::AccessToken,
router_flow_types::{
Expand Down Expand Up @@ -185,3 +188,19 @@ pub type RetrieveFileType =
/// Type alias for `ConnectorIntegration<Defend, DefendDisputeRequestData, DefendDisputeResponse>`
pub type DefendDisputeType =
dyn ConnectorIntegration<Defend, DefendDisputeRequestData, DefendDisputeResponse>;

/// Represents details of a payment method.
#[derive(Debug, Clone)]
pub struct PaymentMethodDetails {
/// The availability status of the payment method based on the environment (e.g., live, beta, upcoming).
pub availability_status: PaymentMethodStage,

/// Indicates whether mandates are supported by this payment method.
pub supports_mandates: bool,
}

/// list of payment method types and metadata related to them
pub type PaymentMethodTypeMetadata = HashMap<PaymentMethodType, PaymentMethodDetails>;

/// list of payment methods, payment method types and metadata related to them
pub type SupportedPaymentMethods = HashMap<PaymentMethod, PaymentMethodTypeMetadata>;
16 changes: 14 additions & 2 deletions crates/router/src/core/payments/flows/authorize_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,21 @@ impl Feature<api::Authorize, types::PaymentsAuthorizeData> for types::PaymentsAu
)
.to_payment_failed_response()?;

if crate::connector::utils::PaymentsAuthorizeRequestData::is_customer_initiated_mandate_payment(
let is_customer_initiated_mandate_payment = crate::connector::utils::PaymentsAuthorizeRequestData::is_customer_initiated_mandate_payment(
&self.request,
) {
);

connector
.connector
.validate_payment_method(
&self.request.payment_method_type,
&self.payment_method,
is_customer_initiated_mandate_payment,
self.test_mode.unwrap_or(false),
)
.to_payment_failed_response()?;

if is_customer_initiated_mandate_payment {
connector
.connector
.validate_mandate_payment(
Expand Down
3 changes: 2 additions & 1 deletion crates/router/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ pub fn mk_app(
.service(routes::ConnectorOnboarding::server(state.clone()))
.service(routes::Verify::server(state.clone()))
.service(routes::Analytics::server(state.clone()))
.service(routes::WebhookEvents::server(state.clone()));
.service(routes::WebhookEvents::server(state.clone()))
.service(routes::FeatureMatrix::server(state.clone()));
}
}

Expand Down
7 changes: 4 additions & 3 deletions crates/router/src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod disputes;
#[cfg(feature = "dummy_connector")]
pub mod dummy_connector;
pub mod ephemeral_key;
pub mod feature_matrix;
pub mod files;
#[cfg(feature = "frm")]
pub mod fraud_check;
Expand Down Expand Up @@ -64,9 +65,9 @@ pub use self::app::DummyConnector;
pub use self::app::Recon;
pub use self::app::{
ApiKeys, AppState, ApplePayCertificatesMigration, Cache, Cards, Configs, ConnectorOnboarding,
Customers, Disputes, EphemeralKey, Files, Forex, Gsm, Health, Mandates, MerchantAccount,
MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll, Profile, ProfileNew,
Refunds, SessionState, User, Webhooks,
Customers, Disputes, EphemeralKey, FeatureMatrix, Files, Forex, Gsm, Health, Mandates,
MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll,
Profile, ProfileNew, Refunds, SessionState, User, Webhooks,
};
#[cfg(feature = "olap")]
pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents};
Expand Down
17 changes: 17 additions & 0 deletions crates/router/src/routes/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ use crate::analytics::AnalyticsProvider;
use crate::errors::RouterResult;
#[cfg(feature = "v1")]
use crate::routes::cards_info::card_iin_info;
#[cfg(all(feature = "olap", feature = "v1"))]
use crate::routes::feature_matrix;
#[cfg(all(feature = "frm", feature = "oltp"))]
use crate::routes::fraud_check as frm_routes;
#[cfg(all(feature = "recon", feature = "olap"))]
Expand Down Expand Up @@ -2144,3 +2146,18 @@ impl WebhookEvents {
)
}
}

#[cfg(feature = "olap")]
pub struct FeatureMatrix;

#[cfg(all(feature = "olap", feature = "v1"))]
impl FeatureMatrix {
pub fn server(state: AppState) -> Scope {
web::scope("/feature_matrix")
.app_data(web::Data::new(state))
.service(
web::resource("")
.route(web::get().to(feature_matrix::fetch_connector_feature_matrix)),
)
}
}
Loading
Loading