mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(router): Add tokenization support for proxy and update the route for proxy (#8530)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -1,3 +1,9 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use error_stack::ResultExt;
|
||||
|
||||
use crate::errors::{CustomResult, ValidationError};
|
||||
|
||||
crate::global_id_type!(
|
||||
GlobalTokenId,
|
||||
"A global id that can be used to identify a token.
|
||||
@ -17,6 +23,15 @@ impl GlobalTokenId {
|
||||
self.0.get_string_repr()
|
||||
}
|
||||
|
||||
///Get GlobalTokenId from a string
|
||||
pub fn from_string(token_string: &str) -> CustomResult<Self, ValidationError> {
|
||||
let token = super::GlobalId::from_string(Cow::Owned(token_string.to_string()))
|
||||
.change_context(ValidationError::IncorrectValueProvided {
|
||||
field_name: "GlobalTokenId",
|
||||
})?;
|
||||
Ok(Self(token))
|
||||
}
|
||||
|
||||
/// Generate a new GlobalTokenId from a cell id
|
||||
pub fn generate(cell_id: &crate::id_type::CellId) -> Self {
|
||||
let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Token);
|
||||
|
||||
@ -3,7 +3,7 @@ use crate::{logger, routes::SessionState, services, types::domain};
|
||||
pub mod utils;
|
||||
use api_models::proxy as proxy_api_models;
|
||||
use common_utils::{
|
||||
ext_traits::{BytesExt, Encode},
|
||||
ext_traits::BytesExt,
|
||||
request::{self, RequestBuilder},
|
||||
};
|
||||
use error_stack::ResultExt;
|
||||
@ -24,25 +24,9 @@ pub async fn proxy_core(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let vault_id = proxy_record.get_vault_id()?;
|
||||
let customer_id = proxy_record.get_customer_id()?;
|
||||
|
||||
let vault_response =
|
||||
super::payment_methods::vault::retrieve_payment_method_from_vault_internal(
|
||||
&state,
|
||||
&merchant_context,
|
||||
&vault_id,
|
||||
&customer_id,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while fetching data from vault")?;
|
||||
|
||||
let vault_data = vault_response
|
||||
.data
|
||||
.encode_to_value()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to serialize vault data")?;
|
||||
let vault_data = proxy_record
|
||||
.get_vault_data(&state, merchant_context)
|
||||
.await?;
|
||||
|
||||
let processed_body =
|
||||
interpolate_token_references_with_vault_data(req.request_body.clone(), &vault_data)?;
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
use api_models::{payment_methods::PaymentMethodId, proxy as proxy_api_models};
|
||||
use common_utils::{ext_traits::OptionExt, id_type};
|
||||
use error_stack::{report, ResultExt};
|
||||
use hyperswitch_domain_models::{
|
||||
errors::api_error_response::NotImplementedMessage, payment_methods,
|
||||
use common_utils::{
|
||||
ext_traits::{Encode, OptionExt},
|
||||
id_type,
|
||||
};
|
||||
use error_stack::ResultExt;
|
||||
use hyperswitch_domain_models::payment_methods;
|
||||
use masking::Mask;
|
||||
use serde_json::Value;
|
||||
use x509_parser::nom::{
|
||||
bytes::complete::{tag, take_while1},
|
||||
character::complete::{char, multispace0},
|
||||
@ -13,9 +15,12 @@ use x509_parser::nom::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
core::errors::{self, RouterResult},
|
||||
core::{
|
||||
errors::{self, RouterResult},
|
||||
payment_methods::vault,
|
||||
},
|
||||
routes::SessionState,
|
||||
types::domain,
|
||||
types::{domain, payment_methods as pm_types},
|
||||
};
|
||||
|
||||
pub struct ProxyRequestWrapper(pub proxy_api_models::ProxyRequest);
|
||||
@ -53,46 +58,23 @@ impl ProxyRequestWrapper {
|
||||
)))
|
||||
}
|
||||
proxy_api_models::TokenType::TokenizationId => {
|
||||
Err(report!(errors::ApiErrorResponse::NotImplemented {
|
||||
message: NotImplementedMessage::Reason(
|
||||
"Proxy flow using tokenization id".to_string(),
|
||||
),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
let token_id = id_type::GlobalTokenId::from_string(token.clone().as_str())
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable(
|
||||
"Error while coneverting from string to GlobalTokenId type",
|
||||
)?;
|
||||
let db = state.store.as_ref();
|
||||
let key_manager_state = &(state).into();
|
||||
|
||||
pub async fn get_customer_id(
|
||||
&self,
|
||||
state: &SessionState,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
storage_scheme: common_enums::enums::MerchantStorageScheme,
|
||||
) -> RouterResult<id_type::GlobalCustomerId> {
|
||||
let token = &self.0.token;
|
||||
|
||||
match self.0.token_type {
|
||||
proxy_api_models::TokenType::PaymentMethodId => {
|
||||
let pm_id = PaymentMethodId {
|
||||
payment_method_id: token.clone(),
|
||||
};
|
||||
let pm_id =
|
||||
id_type::GlobalPaymentMethodId::generate_from_string(pm_id.payment_method_id)
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Unable to generate GlobalPaymentMethodId")?;
|
||||
|
||||
Ok(state
|
||||
.store
|
||||
.find_payment_method(&((state).into()), key_store, &pm_id, storage_scheme)
|
||||
let tokenization_record = db
|
||||
.get_entity_id_vault_id_by_token_id(&token_id, key_store, key_manager_state)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::PaymentMethodNotFound)?
|
||||
.customer_id)
|
||||
}
|
||||
proxy_api_models::TokenType::TokenizationId => {
|
||||
Err(report!(errors::ApiErrorResponse::NotImplemented {
|
||||
message: NotImplementedMessage::Reason(
|
||||
"Proxy flow using tokenization id".to_string(),
|
||||
),
|
||||
}))
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while fetching tokenization record from vault")?;
|
||||
|
||||
Ok(ProxyRecord::TokenizationRecord(Box::new(
|
||||
tokenization_record,
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,7 +98,7 @@ impl ProxyRequestWrapper {
|
||||
}
|
||||
|
||||
impl ProxyRecord {
|
||||
pub fn get_vault_id(&self) -> RouterResult<payment_methods::VaultId> {
|
||||
fn get_vault_id(&self) -> RouterResult<payment_methods::VaultId> {
|
||||
match self {
|
||||
Self::PaymentMethodRecord(payment_method) => payment_method
|
||||
.locker_id
|
||||
@ -124,22 +106,57 @@ impl ProxyRecord {
|
||||
.get_required_value("vault_id")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Locker id not present in Payment Method Entry"),
|
||||
Self::TokenizationRecord(_) => Err(report!(errors::ApiErrorResponse::NotImplemented {
|
||||
message: NotImplementedMessage::Reason(
|
||||
"Proxy flow using tokenization id".to_string(),
|
||||
),
|
||||
})),
|
||||
Self::TokenizationRecord(tokenization_record) => Ok(
|
||||
payment_methods::VaultId::generate(tokenization_record.locker_id.clone()),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_customer_id(&self) -> RouterResult<id_type::GlobalCustomerId> {
|
||||
fn get_customer_id(&self) -> id_type::GlobalCustomerId {
|
||||
match self {
|
||||
Self::PaymentMethodRecord(payment_method) => Ok(payment_method.customer_id.clone()),
|
||||
Self::TokenizationRecord(_) => Err(report!(errors::ApiErrorResponse::NotImplemented {
|
||||
message: NotImplementedMessage::Reason(
|
||||
"Proxy flow using tokenization id".to_string(),
|
||||
),
|
||||
})),
|
||||
Self::PaymentMethodRecord(payment_method) => payment_method.customer_id.clone(),
|
||||
Self::TokenizationRecord(tokenization_record) => {
|
||||
tokenization_record.customer_id.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_vault_data(
|
||||
&self,
|
||||
state: &SessionState,
|
||||
merchant_context: domain::MerchantContext,
|
||||
) -> RouterResult<Value> {
|
||||
match self {
|
||||
Self::PaymentMethodRecord(_) => {
|
||||
let vault_resp = vault::retrieve_payment_method_from_vault_internal(
|
||||
state,
|
||||
&merchant_context,
|
||||
&self.get_vault_id()?,
|
||||
&self.get_customer_id(),
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Error while fetching data from vault")?;
|
||||
|
||||
Ok(vault_resp
|
||||
.data
|
||||
.encode_to_value()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to serialize vault data")?)
|
||||
}
|
||||
Self::TokenizationRecord(_) => {
|
||||
let vault_request = pm_types::VaultRetrieveRequest {
|
||||
entity_id: self.get_customer_id(),
|
||||
vault_id: self.get_vault_id()?,
|
||||
};
|
||||
|
||||
let vault_data = vault::retrieve_value_from_vault(state, vault_request)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
.attach_printable("Failed to retrieve vault data")?;
|
||||
|
||||
Ok(vault_data.get("data").cloned().unwrap_or(Value::Null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -754,7 +754,7 @@ pub struct Proxy;
|
||||
#[cfg(all(feature = "oltp", feature = "v2"))]
|
||||
impl Proxy {
|
||||
pub fn server(state: AppState) -> Scope {
|
||||
web::scope("/proxy")
|
||||
web::scope("/v2/proxy")
|
||||
.app_data(web::Data::new(state))
|
||||
.service(web::resource("").route(web::post().to(proxy::proxy)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user