feat(payment_method): API to list countries and currencies supported by a country and payment method type (#4126)

Co-authored-by: Mani Chandra Dulam <mani.dchandra@juspay.in>
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Amisha Prabhat
2024-03-28 12:30:11 +05:30
committed by GitHub
parent 0f8384dde9
commit 74cd4a7952
9 changed files with 160 additions and 31 deletions

View File

@ -3,8 +3,9 @@ use common_utils::events::{ApiEventMetric, ApiEventsType};
use crate::{
payment_methods::{
CustomerDefaultPaymentMethodResponse, CustomerPaymentMethodsListResponse,
DefaultPaymentMethod, PaymentMethodDeleteResponse, PaymentMethodListRequest,
PaymentMethodListResponse, PaymentMethodResponse, PaymentMethodUpdate,
DefaultPaymentMethod, ListCountriesCurrenciesRequest, ListCountriesCurrenciesResponse,
PaymentMethodDeleteResponse, PaymentMethodListRequest, PaymentMethodListResponse,
PaymentMethodResponse, PaymentMethodUpdate,
},
payments::{
PaymentIdType, PaymentListConstraints, PaymentListFilterConstraints, PaymentListFilters,
@ -131,6 +132,9 @@ impl ApiEventMetric for PaymentMethodListRequest {
}
}
impl ApiEventMetric for ListCountriesCurrenciesRequest {}
impl ApiEventMetric for ListCountriesCurrenciesResponse {}
impl ApiEventMetric for PaymentMethodListResponse {}
impl ApiEventMetric for CustomerDefaultPaymentMethodResponse {

View File

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use cards::CardNumber;
use common_utils::{
@ -914,6 +914,26 @@ pub struct TokenizedCardValue1 {
pub card_token: Option<String>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListCountriesCurrenciesRequest {
pub connector: api_enums::Connector,
pub payment_method_type: api_enums::PaymentMethodType,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ListCountriesCurrenciesResponse {
pub currencies: HashSet<api_enums::Currency>,
pub countries: HashSet<CountryCodeWithName>,
}
#[derive(Debug, serde::Serialize, serde::Deserialize, Eq, Hash, PartialEq)]
pub struct CountryCodeWithName {
pub code: api_enums::CountryAlpha2,
pub name: api_enums::Country,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardValue2 {

View File

@ -1626,6 +1626,7 @@ pub enum DisputeStatus {
serde::Deserialize,
serde::Serialize,
strum::Display,
strum::EnumIter,
strum::EnumString,
utoipa::ToSchema,
Copy

View File

@ -7,8 +7,9 @@ use api_models::{
admin::{self, PaymentMethodsEnabled},
enums::{self as api_enums},
payment_methods::{
BankAccountTokenData, CardDetailsPaymentMethod, CardNetworkTypes,
CustomerDefaultPaymentMethodResponse, MaskedBankDetails, PaymentExperienceTypes,
BankAccountTokenData, CardDetailsPaymentMethod, CardNetworkTypes, CountryCodeWithName,
CustomerDefaultPaymentMethodResponse, ListCountriesCurrenciesRequest,
ListCountriesCurrenciesResponse, MaskedBankDetails, PaymentExperienceTypes,
PaymentMethodsData, RequestPaymentMethodTypes, RequiredFieldInfo,
ResponsePaymentMethodIntermediate, ResponsePaymentMethodTypes,
ResponsePaymentMethodsEnabled,
@ -30,6 +31,7 @@ use domain::CustomerUpdate;
use error_stack::{report, IntoReport, ResultExt};
use masking::Secret;
use router_env::{instrument, tracing};
use strum::IntoEnumIterator;
use super::surcharge_decision_configs::{
perform_surcharge_decision_management_for_payment_method_list,
@ -3606,3 +3608,59 @@ pub async fn create_encrypted_payment_method_data(
pm_data_encrypted
}
pub async fn list_countries_currencies_for_connector_payment_method(
state: routes::AppState,
req: ListCountriesCurrenciesRequest,
) -> errors::RouterResponse<ListCountriesCurrenciesResponse> {
Ok(services::ApplicationResponse::Json(
list_countries_currencies_for_connector_payment_method_util(
state.conf.pm_filters.clone(),
req.connector,
req.payment_method_type,
)
.await,
))
}
// This feature will be more efficient as a WASM function rather than as an API.
// So extracting this logic to a separate function so that it can be used in WASM as well.
pub async fn list_countries_currencies_for_connector_payment_method_util(
connector_filters: settings::ConnectorFilters,
connector: api_enums::Connector,
payment_method_type: api_enums::PaymentMethodType,
) -> ListCountriesCurrenciesResponse {
let payment_method_type =
settings::PaymentMethodFilterKey::PaymentMethodType(payment_method_type);
let (currencies, country_codes) = connector_filters
.0
.get(&connector.to_string())
.and_then(|filter| filter.0.get(&payment_method_type))
.map(|filter| (filter.currency.clone(), filter.country.clone()))
.unwrap_or_else(|| {
connector_filters
.0
.get("default")
.and_then(|filter| filter.0.get(&payment_method_type))
.map_or((None, None), |filter| {
(filter.currency.clone(), filter.country.clone())
})
});
let currencies =
currencies.unwrap_or_else(|| api_enums::Currency::iter().collect::<HashSet<_>>());
let country_codes =
country_codes.unwrap_or_else(|| api_enums::CountryAlpha2::iter().collect::<HashSet<_>>());
ListCountriesCurrenciesResponse {
currencies,
countries: country_codes
.into_iter()
.map(|country_code| CountryCodeWithName {
code: country_code,
name: common_enums::Country::from_alpha2(country_code),
})
.collect(),
}
}

View File

@ -19,8 +19,6 @@ use tokio::sync::oneshot;
#[cfg(feature = "olap")]
use super::blocklist;
#[cfg(any(feature = "olap", feature = "oltp"))]
use super::currency;
#[cfg(feature = "dummy_connector")]
use super::dummy_connector::*;
#[cfg(feature = "payouts")]
@ -39,8 +37,10 @@ use super::{
use super::{cache::*, health::*};
#[cfg(any(feature = "olap", feature = "oltp"))]
use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*};
#[cfg(any(feature = "olap", feature = "oltp"))]
use super::{currency, payment_methods::*};
#[cfg(feature = "oltp")]
use super::{ephemeral_key::*, payment_methods::*, webhooks::*};
use super::{ephemeral_key::*, webhooks::*};
use crate::configs::secrets_transformers;
#[cfg(all(feature = "frm", feature = "oltp"))]
use crate::routes::fraud_check as frm_routes;
@ -738,11 +738,20 @@ impl Payouts {
pub struct PaymentMethods;
#[cfg(feature = "oltp")]
#[cfg(any(feature = "olap", feature = "oltp"))]
impl PaymentMethods {
pub fn server(state: AppState) -> Scope {
web::scope("/payment_methods")
.app_data(web::Data::new(state))
let mut route = web::scope("/payment_methods").app_data(web::Data::new(state));
#[cfg(feature = "olap")]
{
route = route.service(
web::resource("/filter")
.route(web::get().to(list_countries_currencies_for_connector_payment_method)),
);
}
#[cfg(feature = "oltp")]
{
route = route
.service(
web::resource("")
.route(web::post().to(create_payment_method_api))
@ -754,8 +763,14 @@ impl PaymentMethods {
.route(web::post().to(payment_method_update_api))
.route(web::delete().to(payment_method_delete_api)),
)
.service(web::resource("/auth/link").route(web::post().to(pm_auth::link_token_create)))
.service(web::resource("/auth/exchange").route(web::post().to(pm_auth::exchange_token)))
.service(
web::resource("/auth/link").route(web::post().to(pm_auth::link_token_create)),
)
.service(
web::resource("/auth/exchange").route(web::post().to(pm_auth::exchange_token)),
)
}
route
}
}

View File

@ -97,6 +97,7 @@ impl From<Flow> for ApiIdentifier {
| Flow::PaymentMethodsUpdate
| Flow::PaymentMethodsDelete
| Flow::ValidatePaymentMethod
| Flow::ListCountriesCurrencies
| Flow::DefaultPaymentMethodsSet => Self::PaymentMethods,
Flow::PmAuthLinkTokenCreate | Flow::PmAuthExchangeToken => Self::PaymentMethodAuth,

View File

@ -8,7 +8,7 @@ use time::PrimitiveDateTime;
use super::app::AppState;
use crate::{
core::{api_locking, errors, payment_methods::cards},
services::{api, authentication as auth},
services::{api, authentication as auth, authorization::permissions::Permission},
types::{
api::payment_methods::{self, PaymentMethodId},
storage::payment_method::PaymentTokenData,
@ -262,6 +262,35 @@ pub async fn payment_method_delete_api(
.await
}
#[instrument(skip_all, fields(flow = ?Flow::ListCountriesCurrencies))]
pub async fn list_countries_currencies_for_connector_payment_method(
state: web::Data<AppState>,
req: HttpRequest,
query_payload: web::Query<payment_methods::ListCountriesCurrenciesRequest>,
) -> HttpResponse {
let flow = Flow::ListCountriesCurrencies;
let payload = query_payload.into_inner();
Box::pin(api::server_wrap(
flow,
state,
&req,
payload,
|state, _auth: auth::AuthenticationData, req| {
cards::list_countries_currencies_for_connector_payment_method(state, req)
},
#[cfg(not(feature = "release"))]
auth::auth_type(
&auth::ApiKeyAuth,
&auth::JWTAuth(Permission::MerchantConnectorAccountWrite),
req.headers(),
),
#[cfg(feature = "release")]
&auth::JWTAuth(Permission::MerchantConnectorAccountWrite),
api_locking::LockAction::NotApplicable,
))
.await
}
#[instrument(skip_all, fields(flow = ?Flow::DefaultPaymentMethodsSet))]
pub async fn default_payment_method_set_api(
state: web::Data<AppState>,
@ -297,7 +326,6 @@ pub async fn default_payment_method_set_api(
))
.await
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]

View File

@ -1,11 +1,11 @@
pub use api_models::payment_methods::{
CardDetail, CardDetailFromLocker, CardDetailsPaymentMethod, CustomerPaymentMethod,
CustomerPaymentMethodsListResponse, DefaultPaymentMethod, DeleteTokenizeByTokenRequest,
GetTokenizePayloadRequest, GetTokenizePayloadResponse, PaymentMethodCreate,
PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodList, PaymentMethodListRequest,
PaymentMethodListResponse, PaymentMethodResponse, PaymentMethodUpdate, PaymentMethodsData,
TokenizePayloadEncrypted, TokenizePayloadRequest, TokenizedCardValue1, TokenizedCardValue2,
TokenizedWalletValue1, TokenizedWalletValue2,
GetTokenizePayloadRequest, GetTokenizePayloadResponse, ListCountriesCurrenciesRequest,
PaymentMethodCreate, PaymentMethodDeleteResponse, PaymentMethodId, PaymentMethodList,
PaymentMethodListRequest, PaymentMethodListResponse, PaymentMethodResponse,
PaymentMethodUpdate, PaymentMethodsData, TokenizePayloadEncrypted, TokenizePayloadRequest,
TokenizedCardValue1, TokenizedCardValue2, TokenizedWalletValue1, TokenizedWalletValue2,
};
use error_stack::report;

View File

@ -117,6 +117,8 @@ pub enum Flow {
CustomerPaymentMethodsList,
/// List Customers for a merchant
CustomersList,
/// Retrieve countries and currencies for connector and payment method
ListCountriesCurrencies,
/// Payment methods retrieve flow.
PaymentMethodsRetrieve,
/// Payment methods update flow.