mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
customers: Added ephemeral key authentication for customer retrieve (#89)
This commit is contained in:
@ -303,7 +303,9 @@ pub(crate) enum StripeErrorType {
|
|||||||
impl From<ApiErrorResponse> for ErrorCode {
|
impl From<ApiErrorResponse> for ErrorCode {
|
||||||
fn from(value: ApiErrorResponse) -> Self {
|
fn from(value: ApiErrorResponse) -> Self {
|
||||||
match value {
|
match value {
|
||||||
ApiErrorResponse::Unauthorized => ErrorCode::Unauthorized,
|
ApiErrorResponse::Unauthorized | ApiErrorResponse::InvalidEphermeralKey => {
|
||||||
|
ErrorCode::Unauthorized
|
||||||
|
}
|
||||||
ApiErrorResponse::InvalidRequestUrl | ApiErrorResponse::InvalidHttpMethod => {
|
ApiErrorResponse::InvalidRequestUrl | ApiErrorResponse::InvalidHttpMethod => {
|
||||||
ErrorCode::InvalidRequestUrl
|
ErrorCode::InvalidRequestUrl
|
||||||
}
|
}
|
||||||
|
|||||||
@ -109,7 +109,10 @@ impl
|
|||||||
"{}{}{}{}{}",
|
"{}{}{}{}{}",
|
||||||
self.base_url(connectors),
|
self.base_url(connectors),
|
||||||
"v1/payments/",
|
"v1/payments/",
|
||||||
req.request.connector_transaction_id,
|
req.request
|
||||||
|
.connector_transaction_id
|
||||||
|
.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?,
|
||||||
"?entityId=",
|
"?entityId=",
|
||||||
auth.entity_id
|
auth.entity_id
|
||||||
))
|
))
|
||||||
|
|||||||
@ -130,7 +130,7 @@ pub struct AdyenRedirectionAction {
|
|||||||
method: String,
|
method: String,
|
||||||
#[serde(rename = "type")]
|
#[serde(rename = "type")]
|
||||||
type_of_response: String,
|
type_of_response: String,
|
||||||
data: HashMap<String, String>,
|
data: Option<HashMap<String, String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
|
||||||
@ -491,12 +491,21 @@ pub fn get_redirection_response(
|
|||||||
.change_context(errors::ParsingError)
|
.change_context(errors::ParsingError)
|
||||||
.attach_printable("Failed to parse redirection url")?;
|
.attach_printable("Failed to parse redirection url")?;
|
||||||
|
|
||||||
|
let form_field_for_redirection = match response.action.data {
|
||||||
|
Some(data) => data,
|
||||||
|
None => std::collections::HashMap::from_iter(
|
||||||
|
redirection_url_response
|
||||||
|
.query_pairs()
|
||||||
|
.map(|(k, v)| (k.to_string(), v.to_string())),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let redirection_data = services::RedirectForm {
|
let redirection_data = services::RedirectForm {
|
||||||
url: redirection_url_response.to_string(),
|
url: redirection_url_response.to_string(),
|
||||||
method: services::Method::from_str(&response.action.method)
|
method: services::Method::from_str(&response.action.method)
|
||||||
.into_report()
|
.into_report()
|
||||||
.change_context(errors::ParsingError)?,
|
.change_context(errors::ParsingError)?,
|
||||||
form_fields: response.action.data,
|
form_fields: form_field_for_redirection,
|
||||||
};
|
};
|
||||||
|
|
||||||
// We don't get connector transaction id for redirections in Adyen.
|
// We don't get connector transaction id for redirections in Adyen.
|
||||||
|
|||||||
@ -108,7 +108,11 @@ impl
|
|||||||
) -> CustomResult<String, errors::ConnectorError> {
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
let auth_type = braintree::BraintreeAuthType::try_from(&req.connector_auth_type)
|
let auth_type = braintree::BraintreeAuthType::try_from(&req.connector_auth_type)
|
||||||
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
|
||||||
let connector_payment_id = req.request.connector_transaction_id.clone();
|
let connector_payment_id = req
|
||||||
|
.request
|
||||||
|
.connector_transaction_id
|
||||||
|
.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"{}/merchants/{}/transactions/{}",
|
"{}/merchants/{}/transactions/{}",
|
||||||
self.base_url(connectors),
|
self.base_url(connectors),
|
||||||
|
|||||||
@ -107,7 +107,10 @@ impl
|
|||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
self.base_url(connectors),
|
self.base_url(connectors),
|
||||||
"payments/",
|
"payments/",
|
||||||
req.request.connector_transaction_id
|
req.request
|
||||||
|
.connector_transaction_id
|
||||||
|
.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -202,7 +202,8 @@ impl
|
|||||||
"{}{}/{}",
|
"{}{}/{}",
|
||||||
self.base_url(connectors),
|
self.base_url(connectors),
|
||||||
"v1/payment_intents",
|
"v1/payment_intents",
|
||||||
id
|
id.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -286,6 +286,8 @@ pub enum ConnectorError {
|
|||||||
FailedToObtainAuthType,
|
FailedToObtainAuthType,
|
||||||
#[error("This step has not been implemented for: {0}")]
|
#[error("This step has not been implemented for: {0}")]
|
||||||
NotImplemented(String),
|
NotImplemented(String),
|
||||||
|
#[error("Missing connector transaction ID")]
|
||||||
|
MissingConnectorTransactionID,
|
||||||
#[error("Webhooks not implemented for this connector")]
|
#[error("Webhooks not implemented for this connector")]
|
||||||
WebhooksNotImplemented,
|
WebhooksNotImplemented,
|
||||||
#[error("Failed to decode webhook event body")]
|
#[error("Failed to decode webhook event body")]
|
||||||
|
|||||||
@ -28,6 +28,8 @@ pub enum ApiErrorResponse {
|
|||||||
the Dashboard Settings section."
|
the Dashboard Settings section."
|
||||||
)]
|
)]
|
||||||
BadCredentials,
|
BadCredentials,
|
||||||
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_10", message = "Invalid Ephemeral Key for the customer")]
|
||||||
|
InvalidEphermeralKey,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "Unrecognized request URL.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_03", message = "Unrecognized request URL.")]
|
||||||
InvalidRequestUrl,
|
InvalidRequestUrl,
|
||||||
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "The HTTP method is not applicable for this API.")]
|
#[error(error_type = ErrorType::InvalidRequestError, code = "IR_04", message = "The HTTP method is not applicable for this API.")]
|
||||||
@ -137,9 +139,9 @@ impl actix_web::ResponseError for ApiErrorResponse {
|
|||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
ApiErrorResponse::Unauthorized | ApiErrorResponse::BadCredentials => {
|
ApiErrorResponse::Unauthorized
|
||||||
StatusCode::UNAUTHORIZED
|
| ApiErrorResponse::BadCredentials
|
||||||
} // 401
|
| ApiErrorResponse::InvalidEphermeralKey => StatusCode::UNAUTHORIZED, // 401
|
||||||
ApiErrorResponse::InvalidRequestUrl => StatusCode::NOT_FOUND, // 404
|
ApiErrorResponse::InvalidRequestUrl => StatusCode::NOT_FOUND, // 404
|
||||||
ApiErrorResponse::InvalidHttpMethod => StatusCode::METHOD_NOT_ALLOWED, // 405
|
ApiErrorResponse::InvalidHttpMethod => StatusCode::METHOD_NOT_ALLOWED, // 405
|
||||||
ApiErrorResponse::MissingRequiredField { .. }
|
ApiErrorResponse::MissingRequiredField { .. }
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
configs::settings::Server,
|
configs::settings::Server,
|
||||||
|
consts,
|
||||||
core::{
|
core::{
|
||||||
errors::{self, CustomResult, RouterResult, StorageErrorExt},
|
errors::{self, CustomResult, RouterResult, StorageErrorExt},
|
||||||
payment_methods::cards,
|
payment_methods::cards,
|
||||||
@ -21,7 +22,7 @@ use crate::{
|
|||||||
services,
|
services,
|
||||||
types::{
|
types::{
|
||||||
api::{self, enums as api_enums},
|
api::{self, enums as api_enums},
|
||||||
storage::{self, enums as storage_enums},
|
storage::{self, enums as storage_enums, ephemeral_key},
|
||||||
},
|
},
|
||||||
utils::{
|
utils::{
|
||||||
self,
|
self,
|
||||||
@ -571,7 +572,7 @@ pub async fn create_customer_if_not_exist<'a, F: Clone, R>(
|
|||||||
pub async fn make_pm_data<'a, F: Clone, R>(
|
pub async fn make_pm_data<'a, F: Clone, R>(
|
||||||
operation: BoxedOperation<'a, F, R>,
|
operation: BoxedOperation<'a, F, R>,
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
payment_method: Option<storage_enums::PaymentMethodType>,
|
payment_method_type: Option<storage_enums::PaymentMethodType>,
|
||||||
txn_id: &str,
|
txn_id: &str,
|
||||||
_payment_attempt: &storage::PaymentAttempt,
|
_payment_attempt: &storage::PaymentAttempt,
|
||||||
request: &Option<api::PaymentMethod>,
|
request: &Option<api::PaymentMethod>,
|
||||||
@ -579,7 +580,7 @@ pub async fn make_pm_data<'a, F: Clone, R>(
|
|||||||
) -> RouterResult<(BoxedOperation<'a, F, R>, Option<api::PaymentMethod>)> {
|
) -> RouterResult<(BoxedOperation<'a, F, R>, Option<api::PaymentMethod>)> {
|
||||||
let payment_method = match (request, token) {
|
let payment_method = match (request, token) {
|
||||||
(_, Some(token)) => Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(
|
(_, Some(token)) => Ok::<_, error_stack::Report<errors::ApiErrorResponse>>(
|
||||||
if payment_method == Some(storage_enums::PaymentMethodType::Card) {
|
if payment_method_type == Some(storage_enums::PaymentMethodType::Card) {
|
||||||
// TODO: Handle token expiry
|
// TODO: Handle token expiry
|
||||||
Vault::get_payment_method_data_from_locker(state, token).await?
|
Vault::get_payment_method_data_from_locker(state, token).await?
|
||||||
} else {
|
} else {
|
||||||
@ -598,7 +599,13 @@ pub async fn make_pm_data<'a, F: Clone, R>(
|
|||||||
|
|
||||||
let payment_method = match payment_method {
|
let payment_method = match payment_method {
|
||||||
Some(pm) => Some(pm),
|
Some(pm) => Some(pm),
|
||||||
None => Vault::get_payment_method_data_from_locker(state, txn_id).await?,
|
None => {
|
||||||
|
if payment_method_type == Some(storage_enums::PaymentMethodType::Card) {
|
||||||
|
Vault::get_payment_method_data_from_locker(state, txn_id).await?
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((operation, payment_method))
|
Ok((operation, payment_method))
|
||||||
@ -897,6 +904,40 @@ pub fn make_merchant_url_with_response(
|
|||||||
Ok(merchant_url_with_response.to_string())
|
Ok(merchant_url_with_response.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn make_ephemeral_key(
|
||||||
|
state: &AppState,
|
||||||
|
customer_id: String,
|
||||||
|
merchant_id: String,
|
||||||
|
) -> errors::RouterResponse<ephemeral_key::EphemeralKey> {
|
||||||
|
let store = &state.store;
|
||||||
|
let id = utils::generate_id(consts::ID_LENGTH, "eki");
|
||||||
|
let secret = format!("epk_{}", &Uuid::new_v4().simple().to_string());
|
||||||
|
let ek = ephemeral_key::EphemeralKeyNew {
|
||||||
|
id,
|
||||||
|
customer_id,
|
||||||
|
merchant_id,
|
||||||
|
secret,
|
||||||
|
};
|
||||||
|
let ek = store
|
||||||
|
.create_ephemeral_key(ek, state.conf.eph_key.validity)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Unable to create ephemeral key")?;
|
||||||
|
Ok(services::BachResponse::Json(ek))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_ephemeral_key(
|
||||||
|
store: &dyn StorageInterface,
|
||||||
|
ek_id: String,
|
||||||
|
) -> errors::RouterResponse<ephemeral_key::EphemeralKey> {
|
||||||
|
let ek = store
|
||||||
|
.delete_ephemeral_key(&ek_id)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Unable to delete ephemeral key")?;
|
||||||
|
Ok(services::BachResponse::Json(ek))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_pg_redirect_response(
|
pub fn make_pg_redirect_response(
|
||||||
payment_id: String,
|
payment_id: String,
|
||||||
response: &api::PaymentsResponse,
|
response: &api::PaymentsResponse,
|
||||||
|
|||||||
@ -154,12 +154,15 @@ async fn payment_response_ut<F: Clone, T>(
|
|||||||
|
|
||||||
storage::PaymentAttemptUpdate::ResponseUpdate {
|
storage::PaymentAttemptUpdate::ResponseUpdate {
|
||||||
status: router_data.status,
|
status: router_data.status,
|
||||||
connector_transaction_id: Some(
|
connector_transaction_id: match response.resource_id {
|
||||||
response
|
types::ResponseId::NoResponseId => None,
|
||||||
.resource_id
|
_ => Some(
|
||||||
.get_connector_transaction_id()
|
response
|
||||||
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?,
|
.resource_id
|
||||||
),
|
.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?,
|
||||||
|
),
|
||||||
|
},
|
||||||
authentication_type: None,
|
authentication_type: None,
|
||||||
payment_method_id: Some(router_data.payment_method_id),
|
payment_method_id: Some(router_data.payment_method_id),
|
||||||
redirect: Some(response.redirect),
|
redirect: Some(response.redirect),
|
||||||
@ -187,12 +190,15 @@ async fn payment_response_ut<F: Clone, T>(
|
|||||||
.attach_printable("Could not parse the connector response")?;
|
.attach_printable("Could not parse the connector response")?;
|
||||||
|
|
||||||
let connector_response_update = storage::ConnectorResponseUpdate::ResponseUpdate {
|
let connector_response_update = storage::ConnectorResponseUpdate::ResponseUpdate {
|
||||||
connector_transaction_id: Some(
|
connector_transaction_id: match connector_response.resource_id {
|
||||||
connector_response
|
types::ResponseId::NoResponseId => None,
|
||||||
.resource_id
|
_ => Some(
|
||||||
.get_connector_transaction_id()
|
connector_response
|
||||||
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?,
|
.resource_id
|
||||||
),
|
.get_connector_transaction_id()
|
||||||
|
.change_context(errors::ApiErrorResponse::ResourceIdNotFound)?,
|
||||||
|
),
|
||||||
|
},
|
||||||
authentication_data,
|
authentication_data,
|
||||||
encoded_data: payment_data.connector_response.encoded_data.clone(),
|
encoded_data: payment_data.connector_response.encoded_data.clone(),
|
||||||
};
|
};
|
||||||
|
|||||||
@ -376,10 +376,12 @@ impl<F: Clone> TryFrom<PaymentData<F>> for types::PaymentsSyncData {
|
|||||||
|
|
||||||
fn try_from(payment_data: PaymentData<F>) -> Result<Self, Self::Error> {
|
fn try_from(payment_data: PaymentData<F>) -> Result<Self, Self::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
connector_transaction_id: payment_data
|
connector_transaction_id: match payment_data.payment_attempt.connector_transaction_id {
|
||||||
.payment_attempt
|
Some(connector_txn_id) => {
|
||||||
.connector_transaction_id
|
types::ResponseId::ConnectorTransactionId(connector_txn_id)
|
||||||
.ok_or(errors::ApiErrorResponse::SuccessfulPaymentNotFound)?,
|
}
|
||||||
|
None => types::ResponseId::NoResponseId,
|
||||||
|
},
|
||||||
encoded_data: payment_data.connector_response.encoded_data,
|
encoded_data: payment_data.connector_response.encoded_data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,8 +49,8 @@ mod storage {
|
|||||||
let expires = created_at.saturating_add(validity.hours());
|
let expires = created_at.saturating_add(validity.hours());
|
||||||
let created_ek = EphemeralKey {
|
let created_ek = EphemeralKey {
|
||||||
id: new.id,
|
id: new.id,
|
||||||
created_at,
|
created_at: created_at.assume_utc().unix_timestamp(),
|
||||||
expires,
|
expires: expires.assume_utc().unix_timestamp(),
|
||||||
customer_id: new.customer_id,
|
customer_id: new.customer_id,
|
||||||
merchant_id: new.merchant_id,
|
merchant_id: new.merchant_id,
|
||||||
secret: new.secret,
|
secret: new.secret,
|
||||||
|
|||||||
@ -105,6 +105,7 @@ pub fn mk_app(
|
|||||||
.service(routes::PaymentMethods::server(state.clone()))
|
.service(routes::PaymentMethods::server(state.clone()))
|
||||||
.service(routes::MerchantAccount::server(state.clone()))
|
.service(routes::MerchantAccount::server(state.clone()))
|
||||||
.service(routes::MerchantConnectorAccount::server(state.clone()))
|
.service(routes::MerchantConnectorAccount::server(state.clone()))
|
||||||
|
.service(routes::EphemeralKey::server(state.clone()))
|
||||||
.service(routes::Webhooks::server(state.clone()));
|
.service(routes::Webhooks::server(state.clone()));
|
||||||
|
|
||||||
#[cfg(feature = "stripe")]
|
#[cfg(feature = "stripe")]
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
mod admin;
|
mod admin;
|
||||||
mod app;
|
mod app;
|
||||||
mod customers;
|
mod customers;
|
||||||
|
mod ephemeral_key;
|
||||||
mod health;
|
mod health;
|
||||||
mod mandates;
|
mod mandates;
|
||||||
mod metrics;
|
mod metrics;
|
||||||
@ -11,7 +12,7 @@ mod refunds;
|
|||||||
mod webhooks;
|
mod webhooks;
|
||||||
|
|
||||||
pub use self::app::{
|
pub use self::app::{
|
||||||
AppState, Customers, Health, Mandates, MerchantAccount, MerchantConnectorAccount,
|
AppState, Customers, EphemeralKey, Health, Mandates, MerchantAccount, MerchantConnectorAccount,
|
||||||
PaymentMethods, Payments, Payouts, Refunds, Webhooks,
|
PaymentMethods, Payments, Payouts, Refunds, Webhooks,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "stripe")]
|
#[cfg(feature = "stripe")]
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
use actix_web::{web, Scope};
|
use actix_web::{web, Scope};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
admin::*, customers::*, health::*, mandates::*, payment_methods::*, payments::*, payouts::*,
|
admin::*, customers::*, ephemeral_key::*, health::*, mandates::*, payment_methods::*,
|
||||||
refunds::*, webhooks::*,
|
payments::*, payouts::*, refunds::*, webhooks::*,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
configs::settings::Settings,
|
configs::settings::Settings,
|
||||||
@ -189,6 +189,17 @@ impl MerchantConnectorAccount {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct EphemeralKey;
|
||||||
|
|
||||||
|
impl EphemeralKey {
|
||||||
|
pub fn server(config: AppState) -> Scope {
|
||||||
|
web::scope("/ephemeral_keys")
|
||||||
|
.app_data(web::Data::new(config))
|
||||||
|
.service(web::resource("").route(web::post().to(ephemeral_key_create)))
|
||||||
|
.service(web::resource("/{id}").route(web::delete().to(ephemeral_key_delete)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Mandates;
|
pub struct Mandates;
|
||||||
|
|
||||||
impl Mandates {
|
impl Mandates {
|
||||||
|
|||||||
@ -5,7 +5,11 @@ use router_env::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::app::AppState;
|
use super::app::AppState;
|
||||||
use crate::{core::customers::*, services::api, types::api::customers};
|
use crate::{
|
||||||
|
core::customers::*,
|
||||||
|
services::{self, api},
|
||||||
|
types::api::customers,
|
||||||
|
};
|
||||||
|
|
||||||
#[instrument(skip_all, fields(flow = ?Flow::CustomersCreate))]
|
#[instrument(skip_all, fields(flow = ?Flow::CustomersCreate))]
|
||||||
// #[post("")]
|
// #[post("")]
|
||||||
@ -35,12 +39,22 @@ pub async fn customers_retrieve(
|
|||||||
customer_id: path.into_inner(),
|
customer_id: path.into_inner(),
|
||||||
})
|
})
|
||||||
.into_inner();
|
.into_inner();
|
||||||
|
let auth_type = match services::authenticate_eph_key(
|
||||||
|
&req,
|
||||||
|
&*state.store,
|
||||||
|
payload.customer_id.clone(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(auth_type) => auth_type,
|
||||||
|
Err(err) => return api::log_and_return_error_response(err),
|
||||||
|
};
|
||||||
api::server_wrap(
|
api::server_wrap(
|
||||||
&state,
|
&state,
|
||||||
&req,
|
&req,
|
||||||
payload,
|
payload,
|
||||||
|state, merchant_account, req| retrieve_customer(&*state.store, merchant_account, req),
|
|state, merchant_account, req| retrieve_customer(&*state.store, merchant_account, req),
|
||||||
api::MerchantAuthentication::ApiKey,
|
auth_type,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|||||||
44
crates/router/src/routes/ephemeral_key.rs
Normal file
44
crates/router/src/routes/ephemeral_key.rs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
|
use router_env::{
|
||||||
|
tracing::{self, instrument},
|
||||||
|
Flow,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::AppState;
|
||||||
|
use crate::{core::payments::helpers, services::api, types::api::customers};
|
||||||
|
|
||||||
|
#[instrument(skip_all, fields(flow = ?Flow::EphemeralKeyCreate))]
|
||||||
|
pub async fn ephemeral_key_create(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
json_payload: web::Json<customers::CustomerId>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let payload = json_payload.into_inner();
|
||||||
|
api::server_wrap(
|
||||||
|
&state,
|
||||||
|
&req,
|
||||||
|
payload,
|
||||||
|
|state, merchant_account, req| {
|
||||||
|
helpers::make_ephemeral_key(state, req.customer_id, merchant_account.merchant_id)
|
||||||
|
},
|
||||||
|
api::MerchantAuthentication::ApiKey,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all, fields(flow = ?Flow::EphemeralKeyDelete))]
|
||||||
|
pub async fn ephemeral_key_delete(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
path: web::Path<String>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let payload = path.into_inner();
|
||||||
|
api::server_wrap(
|
||||||
|
&state,
|
||||||
|
&req,
|
||||||
|
payload,
|
||||||
|
|state, _, req| helpers::delete_ephemeral_key(&*state.store, req),
|
||||||
|
api::MerchantAuthentication::ApiKey,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@ use crate::{
|
|||||||
storage::{self, enums},
|
storage::{self, enums},
|
||||||
ErrorResponse, Response,
|
ErrorResponse, Response,
|
||||||
},
|
},
|
||||||
utils::OptionExt,
|
utils::{self, OptionExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type BoxedConnectorIntegration<'a, T, Req, Resp> =
|
pub type BoxedConnectorIntegration<'a, T, Req, Resp> =
|
||||||
@ -596,6 +596,29 @@ pub(crate) fn get_auth_type_and_check_client_secret(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn authenticate_eph_key<'a>(
|
||||||
|
req: &'a actix_web::HttpRequest,
|
||||||
|
store: &dyn StorageInterface,
|
||||||
|
customer_id: String,
|
||||||
|
) -> RouterResult<MerchantAuthentication<'a>> {
|
||||||
|
let api_key = get_api_key(req)?;
|
||||||
|
if api_key.starts_with("epk") {
|
||||||
|
let ek = store
|
||||||
|
.get_ephemeral_key(api_key)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::BadCredentials)?;
|
||||||
|
utils::when(
|
||||||
|
ek.customer_id.ne(&customer_id),
|
||||||
|
Err(report!(errors::ApiErrorResponse::InvalidEphermeralKey)),
|
||||||
|
)?;
|
||||||
|
Ok(MerchantAuthentication::MerchantId(Cow::Owned(
|
||||||
|
ek.merchant_id,
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Ok(MerchantAuthentication::ApiKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_api_key(req: &HttpRequest) -> RouterResult<&str> {
|
fn get_api_key(req: &HttpRequest) -> RouterResult<&str> {
|
||||||
req.headers()
|
req.headers()
|
||||||
.get("api-key")
|
.get("api-key")
|
||||||
|
|||||||
@ -104,7 +104,7 @@ pub struct PaymentsCaptureData {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PaymentsSyncData {
|
pub struct PaymentsSyncData {
|
||||||
//TODO : add fields based on the connector requirements
|
//TODO : add fields based on the connector requirements
|
||||||
pub connector_transaction_id: String,
|
pub connector_transaction_id: ResponseId,
|
||||||
pub encoded_data: Option<String>,
|
pub encoded_data: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ pub struct EphemeralKey {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub merchant_id: String,
|
pub merchant_id: String,
|
||||||
pub customer_id: String,
|
pub customer_id: String,
|
||||||
pub created_at: time::PrimitiveDateTime,
|
pub created_at: i64,
|
||||||
pub expires: time::PrimitiveDateTime,
|
pub expires: i64,
|
||||||
pub secret: String,
|
pub secret: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,6 +80,10 @@ pub enum Flow {
|
|||||||
CustomersDelete,
|
CustomersDelete,
|
||||||
/// Customers get mandates flow.
|
/// Customers get mandates flow.
|
||||||
CustomersGetMandates,
|
CustomersGetMandates,
|
||||||
|
/// Create an Ephemeral Key.
|
||||||
|
EphemeralKeyCreate,
|
||||||
|
/// Delete an Ephemeral Key.
|
||||||
|
EphemeralKeyDelete,
|
||||||
/// Mandates retrieve flow.
|
/// Mandates retrieve flow.
|
||||||
MandatesRetrieve,
|
MandatesRetrieve,
|
||||||
/// Mandates revoke flow.
|
/// Mandates revoke flow.
|
||||||
|
|||||||
Reference in New Issue
Block a user