mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 12:15:40 +08:00
feat: cards info api (#749)
Co-authored-by: Jagan <jaganelavarasan@gmail.com> Co-authored-by: Kartikeya Hegde <karthikey.hegde@juspay.in> Co-authored-by: Arun Raj M <jarnura47@gmail.com>
This commit is contained in:
@ -1 +0,0 @@
|
|||||||
|
|
||||||
31
crates/api_models/src/cards_info.rs
Normal file
31
crates/api_models/src/cards_info.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, ToSchema)]
|
||||||
|
pub struct CardsInfoRequestParams {
|
||||||
|
#[schema(example = "pay_OSERgeV9qAy7tlK7aKpc_secret_TuDUoh11Msxh12sXn3Yp")]
|
||||||
|
pub client_secret: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, Debug)]
|
||||||
|
pub struct CardsInfoRequest {
|
||||||
|
pub client_secret: Option<String>,
|
||||||
|
pub card_iin: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, Debug, ToSchema)]
|
||||||
|
pub struct CardInfoResponse {
|
||||||
|
#[schema(example = "374431")]
|
||||||
|
pub card_iin: String,
|
||||||
|
#[schema(example = "AMEX")]
|
||||||
|
pub card_issuer: Option<String>,
|
||||||
|
#[schema(example = "AMEX")]
|
||||||
|
pub card_network: Option<String>,
|
||||||
|
#[schema(example = "CREDIT")]
|
||||||
|
pub card_type: Option<String>,
|
||||||
|
#[schema(example = "CLASSIC")]
|
||||||
|
pub card_sub_type: Option<String>,
|
||||||
|
#[schema(example = "INDIA")]
|
||||||
|
pub card_issuing_country: Option<String>,
|
||||||
|
}
|
||||||
@ -2,7 +2,7 @@
|
|||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod api_keys;
|
pub mod api_keys;
|
||||||
pub mod bank_accounts;
|
pub mod bank_accounts;
|
||||||
pub mod cards;
|
pub mod cards_info;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
pub mod disputes;
|
pub mod disputes;
|
||||||
pub mod enums;
|
pub mod enums;
|
||||||
|
|||||||
@ -347,7 +347,9 @@ impl From<errors::ApiErrorResponse> for StripeErrorCode {
|
|||||||
| errors::ApiErrorResponse::GenericUnauthorized { .. }
|
| errors::ApiErrorResponse::GenericUnauthorized { .. }
|
||||||
| errors::ApiErrorResponse::InvalidEphemeralKey => Self::Unauthorized,
|
| errors::ApiErrorResponse::InvalidEphemeralKey => Self::Unauthorized,
|
||||||
errors::ApiErrorResponse::InvalidRequestUrl
|
errors::ApiErrorResponse::InvalidRequestUrl
|
||||||
| errors::ApiErrorResponse::InvalidHttpMethod => Self::InvalidRequestUrl,
|
| errors::ApiErrorResponse::InvalidHttpMethod
|
||||||
|
| errors::ApiErrorResponse::InvalidCardIin
|
||||||
|
| errors::ApiErrorResponse::InvalidCardIinLength => Self::InvalidRequestUrl,
|
||||||
errors::ApiErrorResponse::MissingRequiredField { field_name } => {
|
errors::ApiErrorResponse::MissingRequiredField { field_name } => {
|
||||||
Self::ParameterMissing {
|
Self::ParameterMissing {
|
||||||
field_name,
|
field_name,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod api_keys;
|
pub mod api_keys;
|
||||||
|
pub mod cards_info;
|
||||||
pub mod configs;
|
pub mod configs;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
|||||||
49
crates/router/src/core/cards_info.rs
Normal file
49
crates/router/src/core/cards_info.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use common_utils::fp_utils::when;
|
||||||
|
use error_stack::{report, ResultExt};
|
||||||
|
use router_env::{instrument, tracing};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
core::{
|
||||||
|
errors::{self, RouterResponse},
|
||||||
|
payments::helpers,
|
||||||
|
},
|
||||||
|
routes,
|
||||||
|
services::ApplicationResponse,
|
||||||
|
types::{storage, transformers::ForeignFrom},
|
||||||
|
};
|
||||||
|
|
||||||
|
fn verify_iin_length(card_iin: &str) -> Result<(), errors::ApiErrorResponse> {
|
||||||
|
let is_bin_length_in_range = card_iin.len() == 6 || card_iin.len() == 8;
|
||||||
|
when(!is_bin_length_in_range, || {
|
||||||
|
Err(errors::ApiErrorResponse::InvalidCardIinLength)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub async fn retrieve_card_info(
|
||||||
|
state: &routes::AppState,
|
||||||
|
merchant_account: storage::MerchantAccount,
|
||||||
|
request: api_models::cards_info::CardsInfoRequest,
|
||||||
|
) -> RouterResponse<api_models::cards_info::CardInfoResponse> {
|
||||||
|
let db = &*state.store;
|
||||||
|
|
||||||
|
verify_iin_length(&request.card_iin)?;
|
||||||
|
helpers::verify_client_secret(
|
||||||
|
db,
|
||||||
|
merchant_account.storage_scheme,
|
||||||
|
request.client_secret,
|
||||||
|
&merchant_account.merchant_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let card_info = db
|
||||||
|
.get_card_info(&request.card_iin)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed to retrieve card information")?
|
||||||
|
.ok_or(report!(errors::ApiErrorResponse::InvalidCardIin))?;
|
||||||
|
|
||||||
|
Ok(ApplicationResponse::Json(
|
||||||
|
api_models::cards_info::CardInfoResponse::foreign_from(card_info),
|
||||||
|
))
|
||||||
|
}
|
||||||
@ -158,6 +158,10 @@ pub enum ApiErrorResponse {
|
|||||||
IncorrectConnectorNameGiven,
|
IncorrectConnectorNameGiven,
|
||||||
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "Address does not exist in our records")]
|
#[error(error_type = ErrorType::ObjectNotFound, code = "HE_04", message = "Address does not exist in our records")]
|
||||||
AddressNotFound,
|
AddressNotFound,
|
||||||
|
#[error(error_type = ErrorType::InvalidRequestError, code = "HE_04", 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")]
|
||||||
|
InvalidCardIinLength,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -202,9 +206,10 @@ impl actix_web::ResponseError for ApiErrorResponse {
|
|||||||
}
|
}
|
||||||
Self::InvalidRequestUrl => StatusCode::NOT_FOUND, // 404
|
Self::InvalidRequestUrl => StatusCode::NOT_FOUND, // 404
|
||||||
Self::InvalidHttpMethod => StatusCode::METHOD_NOT_ALLOWED, // 405
|
Self::InvalidHttpMethod => StatusCode::METHOD_NOT_ALLOWED, // 405
|
||||||
Self::MissingRequiredField { .. } | Self::InvalidDataValue { .. } => {
|
Self::MissingRequiredField { .. }
|
||||||
StatusCode::BAD_REQUEST
|
| Self::InvalidDataValue { .. }
|
||||||
} // 400
|
| Self::InvalidCardIin
|
||||||
|
| Self::InvalidCardIinLength => StatusCode::BAD_REQUEST, // 400
|
||||||
Self::InvalidDataFormat { .. } | Self::InvalidRequestData { .. } => {
|
Self::InvalidDataFormat { .. } | Self::InvalidRequestData { .. } => {
|
||||||
StatusCode::UNPROCESSABLE_ENTITY
|
StatusCode::UNPROCESSABLE_ENTITY
|
||||||
} // 422
|
} // 422
|
||||||
@ -437,9 +442,11 @@ impl common_utils::errors::ErrorSwitch<api_models::errors::types::ApiErrorRespon
|
|||||||
}
|
}
|
||||||
Self::NotSupported { message } => {
|
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("HE", 3, "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 } => {
|
Self::FlowNotSupported { flow, connector } => {
|
||||||
AER::BadRequest(ApiError::new("IR", 20, format!("{flow} flow not supported"), Some(Extra {connector: Some(connector.to_owned()), ..Default::default()})))
|
AER::BadRequest(ApiError::new("IR", 20, format!("{flow} flow not supported"), Some(Extra {connector: Some(connector.to_owned()), ..Default::default()}))) //FIXME: error message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1244,8 +1244,7 @@ pub(crate) async fn verify_client_secret(
|
|||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::PaymentNotFound)?;
|
.change_context(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||||
|
|
||||||
authenticate_client_secret(Some(&cs), payment_intent.client_secret.as_ref())
|
authenticate_client_secret(Some(&cs), payment_intent.client_secret.as_ref())?;
|
||||||
.map_err(errors::ApiErrorResponse::from)?;
|
|
||||||
Ok(payment_intent)
|
Ok(payment_intent)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod api_keys;
|
pub mod api_keys;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
|
pub mod cards_info;
|
||||||
pub mod configs;
|
pub mod configs;
|
||||||
pub mod connector_response;
|
pub mod connector_response;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
@ -55,6 +56,7 @@ pub trait StorageInterface:
|
|||||||
+ queue::QueueInterface
|
+ queue::QueueInterface
|
||||||
+ refund::RefundInterface
|
+ refund::RefundInterface
|
||||||
+ reverse_lookup::ReverseLookupInterface
|
+ reverse_lookup::ReverseLookupInterface
|
||||||
|
+ cards_info::CardsInfoInterface
|
||||||
+ 'static
|
+ 'static
|
||||||
{
|
{
|
||||||
async fn close(&mut self) {}
|
async fn close(&mut self) {}
|
||||||
|
|||||||
41
crates/router/src/db/cards_info.rs
Normal file
41
crates/router/src/db/cards_info.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
use error_stack::IntoReport;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
connection,
|
||||||
|
core::errors::{self, CustomResult},
|
||||||
|
db::MockDb,
|
||||||
|
services::Store,
|
||||||
|
types::storage::cards_info::CardInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
pub trait CardsInfoInterface {
|
||||||
|
async fn get_card_info(
|
||||||
|
&self,
|
||||||
|
_card_iin: &str,
|
||||||
|
) -> CustomResult<Option<CardInfo>, errors::StorageError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl CardsInfoInterface for Store {
|
||||||
|
async fn get_card_info(
|
||||||
|
&self,
|
||||||
|
card_iin: &str,
|
||||||
|
) -> CustomResult<Option<CardInfo>, errors::StorageError> {
|
||||||
|
let conn = connection::pg_connection_read(self).await?;
|
||||||
|
CardInfo::find_by_iin(&conn, card_iin)
|
||||||
|
.await
|
||||||
|
.map_err(Into::into)
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl CardsInfoInterface for MockDb {
|
||||||
|
async fn get_card_info(
|
||||||
|
&self,
|
||||||
|
_card_iin: &str,
|
||||||
|
) -> CustomResult<Option<CardInfo>, errors::StorageError> {
|
||||||
|
Err(errors::StorageError::MockDbError)?
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -121,6 +121,7 @@ pub fn mk_app(
|
|||||||
{
|
{
|
||||||
server_app = server_app.service(routes::StripeApis::server(state.clone()));
|
server_app = server_app.service(routes::StripeApis::server(state.clone()));
|
||||||
}
|
}
|
||||||
|
server_app = server_app.service(routes::Cards::server(state.clone()));
|
||||||
server_app = server_app.service(routes::Health::server(state));
|
server_app = server_app.service(routes::Health::server(state));
|
||||||
server_app
|
server_app
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
pub mod admin;
|
pub mod admin;
|
||||||
pub mod api_keys;
|
pub mod api_keys;
|
||||||
pub mod app;
|
pub mod app;
|
||||||
|
pub mod cards_info;
|
||||||
pub mod configs;
|
pub mod configs;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
pub mod ephemeral_key;
|
pub mod ephemeral_key;
|
||||||
@ -14,7 +15,7 @@ pub mod refunds;
|
|||||||
pub mod webhooks;
|
pub mod webhooks;
|
||||||
|
|
||||||
pub use self::app::{
|
pub use self::app::{
|
||||||
ApiKeys, AppState, Configs, Customers, EphemeralKey, Health, Mandates, MerchantAccount,
|
ApiKeys, AppState, Cards, Configs, Customers, EphemeralKey, Health, Mandates, MerchantAccount,
|
||||||
MerchantConnectorAccount, PaymentMethods, Payments, Payouts, Refunds, Webhooks,
|
MerchantConnectorAccount, PaymentMethods, Payments, Payouts, Refunds, Webhooks,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "stripe")]
|
#[cfg(feature = "stripe")]
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use super::{ephemeral_key::*, payment_methods::*, webhooks::*};
|
|||||||
use crate::{
|
use crate::{
|
||||||
configs::settings::Settings,
|
configs::settings::Settings,
|
||||||
db::{MockDb, StorageImpl, StorageInterface},
|
db::{MockDb, StorageImpl, StorageInterface},
|
||||||
|
routes::cards_info::card_iin_info,
|
||||||
services::Store,
|
services::Store,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -377,3 +378,13 @@ impl ApiKeys {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Cards;
|
||||||
|
|
||||||
|
impl Cards {
|
||||||
|
pub fn server(state: AppState) -> Scope {
|
||||||
|
web::scope("/cards")
|
||||||
|
.app_data(web::Data::new(state))
|
||||||
|
.service(web::resource("/{bin}").route(web::get().to(card_iin_info)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
53
crates/router/src/routes/cards_info.rs
Normal file
53
crates/router/src/routes/cards_info.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use actix_web::{web, HttpRequest, Responder};
|
||||||
|
use router_env::{instrument, tracing, Flow};
|
||||||
|
|
||||||
|
use super::app::AppState;
|
||||||
|
use crate::{
|
||||||
|
core::cards_info,
|
||||||
|
services::{api, authentication as auth},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Cards Info - Retrieve
|
||||||
|
///
|
||||||
|
/// Retrieve the card information given the card bin
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/cards/{bin}",
|
||||||
|
params(("bin" = String, Path, description = "The first 6 or 9 digits of card")),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Card iin data found", body = CardInfoResponse),
|
||||||
|
(status = 404, description = "Card iin data not found")
|
||||||
|
),
|
||||||
|
operation_id = "Retrieve card information",
|
||||||
|
security(("api_key" = []), ("publishable_key" = []))
|
||||||
|
)]
|
||||||
|
#[instrument(skip_all, fields(flow = ?Flow::CardsInfo))]
|
||||||
|
pub async fn card_iin_info(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
path: web::Path<String>,
|
||||||
|
payload: web::Query<api_models::cards_info::CardsInfoRequestParams>,
|
||||||
|
) -> impl Responder {
|
||||||
|
let card_iin = path.into_inner();
|
||||||
|
let request_params = payload.into_inner();
|
||||||
|
|
||||||
|
let payload = api_models::cards_info::CardsInfoRequest {
|
||||||
|
client_secret: request_params.client_secret,
|
||||||
|
card_iin,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (auth, _) = match auth::check_client_secret_and_get_auth(req.headers(), &payload) {
|
||||||
|
Ok((auth, _auth_flow)) => (auth, _auth_flow),
|
||||||
|
Err(e) => return api::log_and_return_error_response(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
api::server_wrap(
|
||||||
|
Flow::CardsInfo,
|
||||||
|
state.as_ref(),
|
||||||
|
&req,
|
||||||
|
payload,
|
||||||
|
cards_info::retrieve_card_info,
|
||||||
|
&*auth,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
@ -33,6 +33,22 @@ where
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ApiKeyAuth;
|
pub struct ApiKeyAuth;
|
||||||
|
|
||||||
|
pub struct NoAuth;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<A> AuthenticateAndFetch<(), A> for NoAuth
|
||||||
|
where
|
||||||
|
A: AppStateInfo + Sync,
|
||||||
|
{
|
||||||
|
async fn authenticate_and_fetch(
|
||||||
|
&self,
|
||||||
|
_request_headers: &HeaderMap,
|
||||||
|
_state: &A,
|
||||||
|
) -> RouterResult<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<A> AuthenticateAndFetch<storage::MerchantAccount, A> for ApiKeyAuth
|
impl<A> AuthenticateAndFetch<storage::MerchantAccount, A> for ApiKeyAuth
|
||||||
where
|
where
|
||||||
@ -243,6 +259,12 @@ impl ClientSecretFetch for PaymentMethodListRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ClientSecretFetch for api_models::cards_info::CardsInfoRequest {
|
||||||
|
fn get_client_secret(&self) -> Option<&String> {
|
||||||
|
self.client_secret.as_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn jwt_auth_or<'a, T, A: AppStateInfo>(
|
pub fn jwt_auth_or<'a, T, A: AppStateInfo>(
|
||||||
default_auth: &'a dyn AuthenticateAndFetch<T, A>,
|
default_auth: &'a dyn AuthenticateAndFetch<T, A>,
|
||||||
headers: &HeaderMap,
|
headers: &HeaderMap,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod api_keys;
|
pub mod api_keys;
|
||||||
|
pub mod cards_info;
|
||||||
pub mod configs;
|
pub mod configs;
|
||||||
pub mod connector_response;
|
pub mod connector_response;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
@ -23,8 +24,8 @@ pub mod refund;
|
|||||||
pub mod kv;
|
pub mod kv;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
address::*, api_keys::*, configs::*, connector_response::*, customers::*, events::*,
|
address::*, api_keys::*, cards_info::*, configs::*, connector_response::*, customers::*,
|
||||||
locker_mock_up::*, mandate::*, merchant_account::*, merchant_connector_account::*,
|
events::*, locker_mock_up::*, mandate::*, merchant_account::*, merchant_connector_account::*,
|
||||||
payment_attempt::*, payment_intent::*, payment_method::*, process_tracker::*, refund::*,
|
payment_attempt::*, payment_intent::*, payment_method::*, process_tracker::*, refund::*,
|
||||||
reverse_lookup::*,
|
reverse_lookup::*,
|
||||||
};
|
};
|
||||||
|
|||||||
1
crates/router/src/types/storage/cards_info.rs
Normal file
1
crates/router/src/types/storage/cards_info.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub use storage_models::cards_info::CardInfo;
|
||||||
@ -435,3 +435,18 @@ impl ForeignFrom<storage_enums::AttemptStatus> for api_enums::AttemptStatus {
|
|||||||
frunk::labelled_convert_from(status)
|
frunk::labelled_convert_from(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ForeignFrom<storage_models::cards_info::CardInfo>
|
||||||
|
for api_models::cards_info::CardInfoResponse
|
||||||
|
{
|
||||||
|
fn foreign_from(item: storage_models::cards_info::CardInfo) -> Self {
|
||||||
|
Self {
|
||||||
|
card_iin: item.card_iin,
|
||||||
|
card_type: item.card_type,
|
||||||
|
card_sub_type: item.card_subtype,
|
||||||
|
card_network: item.card_network,
|
||||||
|
card_issuer: item.card_issuer,
|
||||||
|
card_issuing_country: item.card_issuing_country,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -162,6 +162,8 @@ pub enum Flow {
|
|||||||
ApiKeyRevoke,
|
ApiKeyRevoke,
|
||||||
/// API Key list flow
|
/// API Key list flow
|
||||||
ApiKeyList,
|
ApiKeyList,
|
||||||
|
/// Cards Info flow
|
||||||
|
CardsInfo,
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|||||||
17
crates/storage_models/src/cards_info.rs
Normal file
17
crates/storage_models/src/cards_info.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use diesel::{Identifiable, Queryable};
|
||||||
|
|
||||||
|
use crate::schema::cards_info;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Queryable, Identifiable, serde::Deserialize, serde::Serialize)]
|
||||||
|
#[diesel(table_name = cards_info, primary_key(card_iin))]
|
||||||
|
pub struct CardInfo {
|
||||||
|
pub card_iin: String,
|
||||||
|
pub card_issuer: Option<String>,
|
||||||
|
pub card_network: Option<String>,
|
||||||
|
pub card_type: Option<String>,
|
||||||
|
pub card_subtype: Option<String>,
|
||||||
|
pub card_issuing_country: Option<String>,
|
||||||
|
pub bank_code_id: Option<String>,
|
||||||
|
pub bank_code: Option<String>,
|
||||||
|
pub country_code: Option<String>,
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod api_keys;
|
pub mod api_keys;
|
||||||
|
pub mod cards_info;
|
||||||
pub mod configs;
|
pub mod configs;
|
||||||
pub mod connector_response;
|
pub mod connector_response;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
pub mod address;
|
pub mod address;
|
||||||
pub mod api_keys;
|
pub mod api_keys;
|
||||||
|
pub mod cards_info;
|
||||||
pub mod configs;
|
pub mod configs;
|
||||||
pub mod connector_response;
|
pub mod connector_response;
|
||||||
pub mod customers;
|
pub mod customers;
|
||||||
|
|||||||
13
crates/storage_models/src/query/cards_info.rs
Normal file
13
crates/storage_models/src/query/cards_info.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use diesel::associations::HasTable;
|
||||||
|
|
||||||
|
use crate::{cards_info::CardInfo, query::generics, PgPooledConn, StorageResult};
|
||||||
|
|
||||||
|
impl CardInfo {
|
||||||
|
pub async fn find_by_iin(conn: &PgPooledConn, card_iin: &str) -> StorageResult<Option<Self>> {
|
||||||
|
generics::generic_find_by_id_optional::<<Self as HasTable>::Table, _, _>(
|
||||||
|
conn,
|
||||||
|
card_iin.to_owned(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -42,6 +42,23 @@ diesel::table! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
use diesel::sql_types::*;
|
||||||
|
use crate::enums::diesel_exports::*;
|
||||||
|
|
||||||
|
cards_info (card_iin) {
|
||||||
|
card_iin -> Varchar,
|
||||||
|
card_issuer -> Nullable<Text>,
|
||||||
|
card_network -> Nullable<Text>,
|
||||||
|
card_type -> Nullable<Text>,
|
||||||
|
card_subtype -> Nullable<Text>,
|
||||||
|
card_issuing_country -> Nullable<Text>,
|
||||||
|
bank_code_id -> Nullable<Varchar>,
|
||||||
|
bank_code -> Nullable<Varchar>,
|
||||||
|
country_code -> Nullable<Varchar>,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
use diesel::sql_types::*;
|
use diesel::sql_types::*;
|
||||||
use crate::enums::diesel_exports::*;
|
use crate::enums::diesel_exports::*;
|
||||||
@ -366,6 +383,7 @@ diesel::table! {
|
|||||||
diesel::allow_tables_to_appear_in_same_query!(
|
diesel::allow_tables_to_appear_in_same_query!(
|
||||||
address,
|
address,
|
||||||
api_keys,
|
api_keys,
|
||||||
|
cards_info,
|
||||||
configs,
|
configs,
|
||||||
connector_response,
|
connector_response,
|
||||||
customers,
|
customers,
|
||||||
|
|||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP TABLE IF EXISTS cards_info;
|
||||||
15
migrations/2023-03-14-123541_add_cards_info_table/up.sql
Normal file
15
migrations/2023-03-14-123541_add_cards_info_table/up.sql
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
CREATE TABLE cards_info (
|
||||||
|
card_iin VARCHAR(16) PRIMARY KEY,
|
||||||
|
card_issuer TEXT,
|
||||||
|
card_network TEXT,
|
||||||
|
card_type TEXT,
|
||||||
|
card_subtype TEXT,
|
||||||
|
card_issuing_country TEXT,
|
||||||
|
bank_code_id VARCHAR(32),
|
||||||
|
bank_code VARCHAR(32),
|
||||||
|
country_code VARCHAR(32),
|
||||||
|
date_created TIMESTAMP NOT NULL,
|
||||||
|
last_updated TIMESTAMP,
|
||||||
|
last_updated_provider TEXT
|
||||||
|
)
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user