Skip to content

Commit 767dee9

Browse files
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>
1 parent ede0b4f commit 767dee9

File tree

4 files changed

+95
-79
lines changed

4 files changed

+95
-79
lines changed

crates/common_utils/src/id_type/global_id/token.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
use std::borrow::Cow;
2+
3+
use error_stack::ResultExt;
4+
5+
use crate::errors::{CustomResult, ValidationError};
6+
17
crate::global_id_type!(
28
GlobalTokenId,
39
"A global id that can be used to identify a token.
@@ -17,6 +23,15 @@ impl GlobalTokenId {
1723
self.0.get_string_repr()
1824
}
1925

26+
///Get GlobalTokenId from a string
27+
pub fn from_string(token_string: &str) -> CustomResult<Self, ValidationError> {
28+
let token = super::GlobalId::from_string(Cow::Owned(token_string.to_string()))
29+
.change_context(ValidationError::IncorrectValueProvided {
30+
field_name: "GlobalTokenId",
31+
})?;
32+
Ok(Self(token))
33+
}
34+
2035
/// Generate a new GlobalTokenId from a cell id
2136
pub fn generate(cell_id: &crate::id_type::CellId) -> Self {
2237
let global_id = super::GlobalId::generate(cell_id, super::GlobalEntity::Token);

crates/router/src/core/proxy.rs

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{logger, routes::SessionState, services, types::domain};
33
pub mod utils;
44
use api_models::proxy as proxy_api_models;
55
use common_utils::{
6-
ext_traits::{BytesExt, Encode},
6+
ext_traits::BytesExt,
77
request::{self, RequestBuilder},
88
};
99
use error_stack::ResultExt;
@@ -24,25 +24,9 @@ pub async fn proxy_core(
2424
)
2525
.await?;
2626

27-
let vault_id = proxy_record.get_vault_id()?;
28-
let customer_id = proxy_record.get_customer_id()?;
29-
30-
let vault_response =
31-
super::payment_methods::vault::retrieve_payment_method_from_vault_internal(
32-
&state,
33-
&merchant_context,
34-
&vault_id,
35-
&customer_id,
36-
)
37-
.await
38-
.change_context(errors::ApiErrorResponse::InternalServerError)
39-
.attach_printable("Error while fetching data from vault")?;
40-
41-
let vault_data = vault_response
42-
.data
43-
.encode_to_value()
44-
.change_context(errors::ApiErrorResponse::InternalServerError)
45-
.attach_printable("Failed to serialize vault data")?;
27+
let vault_data = proxy_record
28+
.get_vault_data(&state, merchant_context)
29+
.await?;
4630

4731
let processed_body =
4832
interpolate_token_references_with_vault_data(req.request_body.clone(), &vault_data)?;

crates/router/src/core/proxy/utils.rs

Lines changed: 75 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use api_models::{payment_methods::PaymentMethodId, proxy as proxy_api_models};
2-
use common_utils::{ext_traits::OptionExt, id_type};
3-
use error_stack::{report, ResultExt};
4-
use hyperswitch_domain_models::{
5-
errors::api_error_response::NotImplementedMessage, payment_methods,
2+
use common_utils::{
3+
ext_traits::{Encode, OptionExt},
4+
id_type,
65
};
6+
use error_stack::ResultExt;
7+
use hyperswitch_domain_models::payment_methods;
78
use masking::Mask;
9+
use serde_json::Value;
810
use x509_parser::nom::{
911
bytes::complete::{tag, take_while1},
1012
character::complete::{char, multispace0},
@@ -13,9 +15,12 @@ use x509_parser::nom::{
1315
};
1416

1517
use crate::{
16-
core::errors::{self, RouterResult},
18+
core::{
19+
errors::{self, RouterResult},
20+
payment_methods::vault,
21+
},
1722
routes::SessionState,
18-
types::domain,
23+
types::{domain, payment_methods as pm_types},
1924
};
2025

2126
pub struct ProxyRequestWrapper(pub proxy_api_models::ProxyRequest);
@@ -53,46 +58,23 @@ impl ProxyRequestWrapper {
5358
)))
5459
}
5560
proxy_api_models::TokenType::TokenizationId => {
56-
Err(report!(errors::ApiErrorResponse::NotImplemented {
57-
message: NotImplementedMessage::Reason(
58-
"Proxy flow using tokenization id".to_string(),
59-
),
60-
}))
61-
}
62-
}
63-
}
64-
65-
pub async fn get_customer_id(
66-
&self,
67-
state: &SessionState,
68-
key_store: &domain::MerchantKeyStore,
69-
storage_scheme: common_enums::enums::MerchantStorageScheme,
70-
) -> RouterResult<id_type::GlobalCustomerId> {
71-
let token = &self.0.token;
72-
73-
match self.0.token_type {
74-
proxy_api_models::TokenType::PaymentMethodId => {
75-
let pm_id = PaymentMethodId {
76-
payment_method_id: token.clone(),
77-
};
78-
let pm_id =
79-
id_type::GlobalPaymentMethodId::generate_from_string(pm_id.payment_method_id)
80-
.change_context(errors::ApiErrorResponse::InternalServerError)
81-
.attach_printable("Unable to generate GlobalPaymentMethodId")?;
82-
83-
Ok(state
84-
.store
85-
.find_payment_method(&((state).into()), key_store, &pm_id, storage_scheme)
61+
let token_id = id_type::GlobalTokenId::from_string(token.clone().as_str())
62+
.change_context(errors::ApiErrorResponse::InternalServerError)
63+
.attach_printable(
64+
"Error while coneverting from string to GlobalTokenId type",
65+
)?;
66+
let db = state.store.as_ref();
67+
let key_manager_state = &(state).into();
68+
69+
let tokenization_record = db
70+
.get_entity_id_vault_id_by_token_id(&token_id, key_store, key_manager_state)
8671
.await
87-
.change_context(errors::ApiErrorResponse::PaymentMethodNotFound)?
88-
.customer_id)
89-
}
90-
proxy_api_models::TokenType::TokenizationId => {
91-
Err(report!(errors::ApiErrorResponse::NotImplemented {
92-
message: NotImplementedMessage::Reason(
93-
"Proxy flow using tokenization id".to_string(),
94-
),
95-
}))
72+
.change_context(errors::ApiErrorResponse::InternalServerError)
73+
.attach_printable("Error while fetching tokenization record from vault")?;
74+
75+
Ok(ProxyRecord::TokenizationRecord(Box::new(
76+
tokenization_record,
77+
)))
9678
}
9779
}
9880
}
@@ -116,30 +98,65 @@ impl ProxyRequestWrapper {
11698
}
11799

118100
impl ProxyRecord {
119-
pub fn get_vault_id(&self) -> RouterResult<payment_methods::VaultId> {
101+
fn get_vault_id(&self) -> RouterResult<payment_methods::VaultId> {
120102
match self {
121103
Self::PaymentMethodRecord(payment_method) => payment_method
122104
.locker_id
123105
.clone()
124106
.get_required_value("vault_id")
125107
.change_context(errors::ApiErrorResponse::InternalServerError)
126108
.attach_printable("Locker id not present in Payment Method Entry"),
127-
Self::TokenizationRecord(_) => Err(report!(errors::ApiErrorResponse::NotImplemented {
128-
message: NotImplementedMessage::Reason(
129-
"Proxy flow using tokenization id".to_string(),
130-
),
131-
})),
109+
Self::TokenizationRecord(tokenization_record) => Ok(
110+
payment_methods::VaultId::generate(tokenization_record.locker_id.clone()),
111+
),
132112
}
133113
}
134114

135-
pub fn get_customer_id(&self) -> RouterResult<id_type::GlobalCustomerId> {
115+
fn get_customer_id(&self) -> id_type::GlobalCustomerId {
136116
match self {
137-
Self::PaymentMethodRecord(payment_method) => Ok(payment_method.customer_id.clone()),
138-
Self::TokenizationRecord(_) => Err(report!(errors::ApiErrorResponse::NotImplemented {
139-
message: NotImplementedMessage::Reason(
140-
"Proxy flow using tokenization id".to_string(),
141-
),
142-
})),
117+
Self::PaymentMethodRecord(payment_method) => payment_method.customer_id.clone(),
118+
Self::TokenizationRecord(tokenization_record) => {
119+
tokenization_record.customer_id.clone()
120+
}
121+
}
122+
}
123+
124+
pub async fn get_vault_data(
125+
&self,
126+
state: &SessionState,
127+
merchant_context: domain::MerchantContext,
128+
) -> RouterResult<Value> {
129+
match self {
130+
Self::PaymentMethodRecord(_) => {
131+
let vault_resp = vault::retrieve_payment_method_from_vault_internal(
132+
state,
133+
&merchant_context,
134+
&self.get_vault_id()?,
135+
&self.get_customer_id(),
136+
)
137+
.await
138+
.change_context(errors::ApiErrorResponse::InternalServerError)
139+
.attach_printable("Error while fetching data from vault")?;
140+
141+
Ok(vault_resp
142+
.data
143+
.encode_to_value()
144+
.change_context(errors::ApiErrorResponse::InternalServerError)
145+
.attach_printable("Failed to serialize vault data")?)
146+
}
147+
Self::TokenizationRecord(_) => {
148+
let vault_request = pm_types::VaultRetrieveRequest {
149+
entity_id: self.get_customer_id(),
150+
vault_id: self.get_vault_id()?,
151+
};
152+
153+
let vault_data = vault::retrieve_value_from_vault(state, vault_request)
154+
.await
155+
.change_context(errors::ApiErrorResponse::InternalServerError)
156+
.attach_printable("Failed to retrieve vault data")?;
157+
158+
Ok(vault_data.get("data").cloned().unwrap_or(Value::Null))
159+
}
143160
}
144161
}
145162
}

crates/router/src/routes/app.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ pub struct Proxy;
754754
#[cfg(all(feature = "oltp", feature = "v2"))]
755755
impl Proxy {
756756
pub fn server(state: AppState) -> Scope {
757-
web::scope("/proxy")
757+
web::scope("/v2/proxy")
758758
.app_data(web::Data::new(state))
759759
.service(web::resource("").route(web::post().to(proxy::proxy)))
760760
}

0 commit comments

Comments
 (0)