mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 04:04:55 +08:00
feat(core): add hypersense integration api (#7218)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -2,6 +2,7 @@ pub mod apple_pay_certificates_migration;
|
|||||||
pub mod connector_onboarding;
|
pub mod connector_onboarding;
|
||||||
pub mod customer;
|
pub mod customer;
|
||||||
pub mod dispute;
|
pub mod dispute;
|
||||||
|
pub mod external_service_auth;
|
||||||
pub mod gsm;
|
pub mod gsm;
|
||||||
mod locker_migration;
|
mod locker_migration;
|
||||||
pub mod payment;
|
pub mod payment;
|
||||||
|
|||||||
30
crates/api_models/src/events/external_service_auth.rs
Normal file
30
crates/api_models/src/events/external_service_auth.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use common_utils::events::{ApiEventMetric, ApiEventsType};
|
||||||
|
|
||||||
|
use crate::external_service_auth::{
|
||||||
|
ExternalSignoutTokenRequest, ExternalTokenResponse, ExternalVerifyTokenRequest,
|
||||||
|
ExternalVerifyTokenResponse,
|
||||||
|
};
|
||||||
|
|
||||||
|
impl ApiEventMetric for ExternalTokenResponse {
|
||||||
|
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||||
|
Some(ApiEventsType::ExternalServiceAuth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiEventMetric for ExternalVerifyTokenRequest {
|
||||||
|
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||||
|
Some(ApiEventsType::ExternalServiceAuth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiEventMetric for ExternalVerifyTokenResponse {
|
||||||
|
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||||
|
Some(ApiEventsType::ExternalServiceAuth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ApiEventMetric for ExternalSignoutTokenRequest {
|
||||||
|
fn get_api_event_type(&self) -> Option<ApiEventsType> {
|
||||||
|
Some(ApiEventsType::ExternalServiceAuth)
|
||||||
|
}
|
||||||
|
}
|
||||||
35
crates/api_models/src/external_service_auth.rs
Normal file
35
crates/api_models/src/external_service_auth.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use common_utils::{id_type, pii};
|
||||||
|
use masking::Secret;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
pub struct ExternalTokenResponse {
|
||||||
|
pub token: Secret<String>,
|
||||||
|
}
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct ExternalVerifyTokenRequest {
|
||||||
|
pub token: Secret<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct ExternalSignoutTokenRequest {
|
||||||
|
pub token: Secret<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize, Debug)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum ExternalVerifyTokenResponse {
|
||||||
|
Hypersense {
|
||||||
|
user_id: String,
|
||||||
|
merchant_id: id_type::MerchantId,
|
||||||
|
name: Secret<String>,
|
||||||
|
email: pii::Email,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExternalVerifyTokenResponse {
|
||||||
|
pub fn get_user_id(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
Self::Hypersense { user_id, .. } => user_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,6 +16,7 @@ pub mod ephemeral_key;
|
|||||||
#[cfg(feature = "errors")]
|
#[cfg(feature = "errors")]
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
pub mod external_service_auth;
|
||||||
pub mod feature_matrix;
|
pub mod feature_matrix;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
pub mod gsm;
|
pub mod gsm;
|
||||||
|
|||||||
@ -99,6 +99,7 @@ pub enum ApiEventsType {
|
|||||||
ApplePayCertificatesMigration,
|
ApplePayCertificatesMigration,
|
||||||
FraudCheck,
|
FraudCheck,
|
||||||
Recon,
|
Recon,
|
||||||
|
ExternalServiceAuth,
|
||||||
Dispute {
|
Dispute {
|
||||||
dispute_id: String,
|
dispute_id: String,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -18,6 +18,7 @@ pub mod customers;
|
|||||||
pub mod disputes;
|
pub mod disputes;
|
||||||
pub mod encryption;
|
pub mod encryption;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
pub mod external_service_auth;
|
||||||
pub mod files;
|
pub mod files;
|
||||||
#[cfg(feature = "frm")]
|
#[cfg(feature = "frm")]
|
||||||
pub mod fraud_check;
|
pub mod fraud_check;
|
||||||
|
|||||||
94
crates/router/src/core/external_service_auth.rs
Normal file
94
crates/router/src/core/external_service_auth.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use api_models::external_service_auth as external_service_auth_api;
|
||||||
|
use common_utils::fp_utils;
|
||||||
|
use error_stack::ResultExt;
|
||||||
|
use masking::ExposeInterface;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
core::errors::{self, RouterResponse},
|
||||||
|
services::{
|
||||||
|
api as service_api,
|
||||||
|
authentication::{self, ExternalServiceType, ExternalToken},
|
||||||
|
},
|
||||||
|
SessionState,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn generate_external_token(
|
||||||
|
state: SessionState,
|
||||||
|
user: authentication::UserFromToken,
|
||||||
|
external_service_type: ExternalServiceType,
|
||||||
|
) -> RouterResponse<external_service_auth_api::ExternalTokenResponse> {
|
||||||
|
let token = ExternalToken::new_token(
|
||||||
|
user.user_id.clone(),
|
||||||
|
user.merchant_id.clone(),
|
||||||
|
&state.conf,
|
||||||
|
external_service_type.clone(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable_lazy(|| {
|
||||||
|
format!(
|
||||||
|
"Failed to create external token for params [user_id, mid, external_service_type] [{}, {:?}, {:?}]",
|
||||||
|
user.user_id, user.merchant_id, external_service_type,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(service_api::ApplicationResponse::Json(
|
||||||
|
external_service_auth_api::ExternalTokenResponse {
|
||||||
|
token: token.into(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signout_external_token(
|
||||||
|
state: SessionState,
|
||||||
|
json_payload: external_service_auth_api::ExternalSignoutTokenRequest,
|
||||||
|
) -> RouterResponse<()> {
|
||||||
|
let token = authentication::decode_jwt::<ExternalToken>(&json_payload.token.expose(), &state)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::Unauthorized)?;
|
||||||
|
|
||||||
|
authentication::blacklist::insert_user_in_blacklist(&state, &token.user_id)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InvalidJwtToken)?;
|
||||||
|
|
||||||
|
Ok(service_api::ApplicationResponse::StatusOk)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn verify_external_token(
|
||||||
|
state: SessionState,
|
||||||
|
json_payload: external_service_auth_api::ExternalVerifyTokenRequest,
|
||||||
|
external_service_type: ExternalServiceType,
|
||||||
|
) -> RouterResponse<external_service_auth_api::ExternalVerifyTokenResponse> {
|
||||||
|
let token_from_payload = json_payload.token.expose();
|
||||||
|
|
||||||
|
let token = authentication::decode_jwt::<ExternalToken>(&token_from_payload, &state)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::Unauthorized)?;
|
||||||
|
|
||||||
|
fp_utils::when(
|
||||||
|
authentication::blacklist::check_user_in_blacklist(&state, &token.user_id, token.exp)
|
||||||
|
.await?,
|
||||||
|
|| Err(errors::ApiErrorResponse::InvalidJwtToken),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
token.check_service_type(&external_service_type)?;
|
||||||
|
|
||||||
|
let user_in_db = state
|
||||||
|
.global_store
|
||||||
|
.find_user_by_id(&token.user_id)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("User not found in database")?;
|
||||||
|
|
||||||
|
let email = user_in_db.email.clone();
|
||||||
|
let name = user_in_db.name;
|
||||||
|
|
||||||
|
Ok(service_api::ApplicationResponse::Json(
|
||||||
|
external_service_auth_api::ExternalVerifyTokenResponse::Hypersense {
|
||||||
|
user_id: user_in_db.user_id,
|
||||||
|
merchant_id: token.merchant_id,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
@ -144,6 +144,7 @@ pub fn mk_app(
|
|||||||
.service(routes::MerchantConnectorAccount::server(state.clone()))
|
.service(routes::MerchantConnectorAccount::server(state.clone()))
|
||||||
.service(routes::RelayWebhooks::server(state.clone()))
|
.service(routes::RelayWebhooks::server(state.clone()))
|
||||||
.service(routes::Webhooks::server(state.clone()))
|
.service(routes::Webhooks::server(state.clone()))
|
||||||
|
.service(routes::Hypersense::server(state.clone()))
|
||||||
.service(routes::Relay::server(state.clone()));
|
.service(routes::Relay::server(state.clone()));
|
||||||
|
|
||||||
#[cfg(feature = "oltp")]
|
#[cfg(feature = "oltp")]
|
||||||
|
|||||||
@ -23,6 +23,7 @@ pub mod files;
|
|||||||
pub mod fraud_check;
|
pub mod fraud_check;
|
||||||
pub mod gsm;
|
pub mod gsm;
|
||||||
pub mod health;
|
pub mod health;
|
||||||
|
pub mod hypersense;
|
||||||
pub mod lock_utils;
|
pub mod lock_utils;
|
||||||
#[cfg(feature = "v1")]
|
#[cfg(feature = "v1")]
|
||||||
pub mod locker_migration;
|
pub mod locker_migration;
|
||||||
@ -69,9 +70,9 @@ pub use self::app::PaymentMethodsSession;
|
|||||||
pub use self::app::Recon;
|
pub use self::app::Recon;
|
||||||
pub use self::app::{
|
pub use self::app::{
|
||||||
ApiKeys, AppState, ApplePayCertificatesMigration, Cache, Cards, Configs, ConnectorOnboarding,
|
ApiKeys, AppState, ApplePayCertificatesMigration, Cache, Cards, Configs, ConnectorOnboarding,
|
||||||
Customers, Disputes, EphemeralKey, FeatureMatrix, Files, Forex, Gsm, Health, Mandates,
|
Customers, Disputes, EphemeralKey, FeatureMatrix, Files, Forex, Gsm, Health, Hypersense,
|
||||||
MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments, Poll,
|
Mandates, MerchantAccount, MerchantConnectorAccount, PaymentLink, PaymentMethods, Payments,
|
||||||
Profile, ProfileNew, Refunds, Relay, RelayWebhooks, SessionState, User, Webhooks,
|
Poll, Profile, ProfileNew, Refunds, Relay, RelayWebhooks, SessionState, User, Webhooks,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents};
|
pub use self::app::{Blocklist, Organization, Routing, Verify, WebhookEvents};
|
||||||
|
|||||||
@ -88,6 +88,7 @@ pub use crate::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
configs::{secrets_transformers, Settings},
|
configs::{secrets_transformers, Settings},
|
||||||
db::kafka_store::{KafkaStore, TenantID},
|
db::kafka_store::{KafkaStore, TenantID},
|
||||||
|
routes::hypersense as hypersense_routes,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -1330,6 +1331,27 @@ impl Recon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Hypersense;
|
||||||
|
|
||||||
|
impl Hypersense {
|
||||||
|
pub fn server(state: AppState) -> Scope {
|
||||||
|
web::scope("/hypersense")
|
||||||
|
.app_data(web::Data::new(state))
|
||||||
|
.service(
|
||||||
|
web::resource("/token")
|
||||||
|
.route(web::get().to(hypersense_routes::get_hypersense_token)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/verify_token")
|
||||||
|
.route(web::post().to(hypersense_routes::verify_hypersense_token)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/signout")
|
||||||
|
.route(web::post().to(hypersense_routes::signout_hypersense_token)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
pub struct Blocklist;
|
pub struct Blocklist;
|
||||||
|
|
||||||
|
|||||||
76
crates/router/src/routes/hypersense.rs
Normal file
76
crates/router/src/routes/hypersense.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
use actix_web::{web, HttpRequest, HttpResponse};
|
||||||
|
use api_models::external_service_auth as external_service_auth_api;
|
||||||
|
use router_env::Flow;
|
||||||
|
|
||||||
|
use super::AppState;
|
||||||
|
use crate::{
|
||||||
|
core::{api_locking, external_service_auth},
|
||||||
|
services::{
|
||||||
|
api,
|
||||||
|
authentication::{self, ExternalServiceType},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn get_hypersense_token(state: web::Data<AppState>, req: HttpRequest) -> HttpResponse {
|
||||||
|
let flow = Flow::HypersenseTokenRequest;
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state,
|
||||||
|
&req,
|
||||||
|
(),
|
||||||
|
|state, user, _, _| {
|
||||||
|
external_service_auth::generate_external_token(
|
||||||
|
state,
|
||||||
|
user,
|
||||||
|
ExternalServiceType::Hypersense,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
&authentication::DashboardNoPermissionAuth,
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn signout_hypersense_token(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
http_req: HttpRequest,
|
||||||
|
json_payload: web::Json<external_service_auth_api::ExternalSignoutTokenRequest>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::HypersenseSignoutToken;
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state.clone(),
|
||||||
|
&http_req,
|
||||||
|
json_payload.into_inner(),
|
||||||
|
|state, _: (), json_payload, _| {
|
||||||
|
external_service_auth::signout_external_token(state, json_payload)
|
||||||
|
},
|
||||||
|
&authentication::NoAuth,
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn verify_hypersense_token(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
http_req: HttpRequest,
|
||||||
|
json_payload: web::Json<external_service_auth_api::ExternalVerifyTokenRequest>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::HypersenseVerifyToken;
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state.clone(),
|
||||||
|
&http_req,
|
||||||
|
json_payload.into_inner(),
|
||||||
|
|state, _: (), json_payload, _| {
|
||||||
|
external_service_auth::verify_external_token(
|
||||||
|
state,
|
||||||
|
json_payload,
|
||||||
|
ExternalServiceType::Hypersense,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
&authentication::NoAuth,
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
@ -39,6 +39,7 @@ pub enum ApiIdentifier {
|
|||||||
ApplePayCertificatesMigration,
|
ApplePayCertificatesMigration,
|
||||||
Relay,
|
Relay,
|
||||||
Documentation,
|
Documentation,
|
||||||
|
Hypersense,
|
||||||
PaymentMethodsSession,
|
PaymentMethodsSession,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +312,10 @@ impl From<Flow> for ApiIdentifier {
|
|||||||
|
|
||||||
Flow::FeatureMatrix => Self::Documentation,
|
Flow::FeatureMatrix => Self::Documentation,
|
||||||
|
|
||||||
|
Flow::HypersenseTokenRequest
|
||||||
|
| Flow::HypersenseVerifyToken
|
||||||
|
| Flow::HypersenseSignoutToken => Self::Hypersense,
|
||||||
|
|
||||||
Flow::PaymentMethodSessionCreate
|
Flow::PaymentMethodSessionCreate
|
||||||
| Flow::PaymentMethodSessionRetrieve
|
| Flow::PaymentMethodSessionRetrieve
|
||||||
| Flow::PaymentMethodSessionUpdateSavedPaymentMethod => Self::PaymentMethodsSession,
|
| Flow::PaymentMethodSessionUpdateSavedPaymentMethod => Self::PaymentMethodsSession,
|
||||||
|
|||||||
@ -13,9 +13,7 @@ use api_models::payouts;
|
|||||||
use api_models::{payment_methods::PaymentMethodListRequest, payments};
|
use api_models::{payment_methods::PaymentMethodListRequest, payments};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use common_enums::TokenPurpose;
|
use common_enums::TokenPurpose;
|
||||||
#[cfg(feature = "v2")]
|
use common_utils::{date_time, fp_utils, id_type};
|
||||||
use common_utils::fp_utils;
|
|
||||||
use common_utils::{date_time, id_type};
|
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
use diesel_models::ephemeral_key;
|
use diesel_models::ephemeral_key;
|
||||||
use error_stack::{report, ResultExt};
|
use error_stack::{report, ResultExt};
|
||||||
@ -195,6 +193,13 @@ impl AuthenticationType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Serialize, serde::Deserialize, strum::Display)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum ExternalServiceType {
|
||||||
|
Hypersense,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct UserFromSinglePurposeToken {
|
pub struct UserFromSinglePurposeToken {
|
||||||
@ -3857,3 +3862,44 @@ impl ReconToken {
|
|||||||
jwt::generate_jwt(&token_payload, settings).await
|
jwt::generate_jwt(&token_payload, settings).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct ExternalToken {
|
||||||
|
pub user_id: String,
|
||||||
|
pub merchant_id: id_type::MerchantId,
|
||||||
|
pub exp: u64,
|
||||||
|
pub external_service_type: ExternalServiceType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExternalToken {
|
||||||
|
pub async fn new_token(
|
||||||
|
user_id: String,
|
||||||
|
merchant_id: id_type::MerchantId,
|
||||||
|
settings: &Settings,
|
||||||
|
external_service_type: ExternalServiceType,
|
||||||
|
) -> UserResult<String> {
|
||||||
|
let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS);
|
||||||
|
let exp = jwt::generate_exp(exp_duration)?.as_secs();
|
||||||
|
|
||||||
|
let token_payload = Self {
|
||||||
|
user_id,
|
||||||
|
merchant_id,
|
||||||
|
exp,
|
||||||
|
external_service_type,
|
||||||
|
};
|
||||||
|
jwt::generate_jwt(&token_payload, settings).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check_service_type(
|
||||||
|
&self,
|
||||||
|
required_service_type: &ExternalServiceType,
|
||||||
|
) -> RouterResult<()> {
|
||||||
|
Ok(fp_utils::when(
|
||||||
|
&self.external_service_type != required_service_type,
|
||||||
|
|| {
|
||||||
|
Err(errors::ApiErrorResponse::AccessForbidden {
|
||||||
|
resource: required_service_type.to_string(),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -543,6 +543,12 @@ pub enum Flow {
|
|||||||
RelayRetrieve,
|
RelayRetrieve,
|
||||||
/// Incoming Relay Webhook Receive
|
/// Incoming Relay Webhook Receive
|
||||||
IncomingRelayWebhookReceive,
|
IncomingRelayWebhookReceive,
|
||||||
|
/// Generate Hypersense Token
|
||||||
|
HypersenseTokenRequest,
|
||||||
|
/// Verify Hypersense Token
|
||||||
|
HypersenseVerifyToken,
|
||||||
|
/// Signout Hypersense Token
|
||||||
|
HypersenseSignoutToken,
|
||||||
/// Payment Method Session Create
|
/// Payment Method Session Create
|
||||||
PaymentMethodSessionCreate,
|
PaymentMethodSessionCreate,
|
||||||
/// Payment Method Session Retrieve
|
/// Payment Method Session Retrieve
|
||||||
|
|||||||
Reference in New Issue
Block a user