mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(core): [NETWORK TOKENIZATION] Check Network Token Status API (#9443)
This commit is contained in:
@ -284,6 +284,12 @@
|
|||||||
"v2/payment-methods/list-saved-payment-methods-for-a-customer"
|
"v2/payment-methods/list-saved-payment-methods-for-a-customer"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"group": "Network Tokenization",
|
||||||
|
"pages": [
|
||||||
|
"v2/payment-methods/check-network-token-status"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"group": "Payment Method Session",
|
"group": "Payment Method Session",
|
||||||
"pages": [
|
"pages": [
|
||||||
|
|||||||
@ -2891,6 +2891,47 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/v2/payment-methods/{payment_method_id}/check-network-token-status": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Payment Methods"
|
||||||
|
],
|
||||||
|
"summary": "Payment Method - Check Network Token Status",
|
||||||
|
"description": "Check the status of a network token for a saved payment method",
|
||||||
|
"operationId": "Check Network Token Status",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "payment_method_id",
|
||||||
|
"in": "path",
|
||||||
|
"description": "The unique identifier for the Payment Method",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Network Token Status Retrieved",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/NetworkTokenStatusCheckResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Payment Method Not Found"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"api_key": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"/v2/customers/{id}/saved-payment-methods": {
|
"/v2/customers/{id}/saved-payment-methods": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -15305,6 +15346,115 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"NetworkTokenStatusCheckFailureResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"error_message"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"error_message": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Error message describing what went wrong"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NetworkTokenStatusCheckResponse": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/NetworkTokenStatusCheckSuccessResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"success_response"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/NetworkTokenStatusCheckFailureResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"failure_response"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"discriminator": {
|
||||||
|
"propertyName": "type"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NetworkTokenStatusCheckSuccessResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"status",
|
||||||
|
"token_expiry_month",
|
||||||
|
"token_expiry_year",
|
||||||
|
"card_last_four",
|
||||||
|
"token_last_four",
|
||||||
|
"card_expiry",
|
||||||
|
"payment_method_id",
|
||||||
|
"customer_id"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"$ref": "#/components/schemas/TokenStatus"
|
||||||
|
},
|
||||||
|
"token_expiry_month": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The expiry month of the network token if active"
|
||||||
|
},
|
||||||
|
"token_expiry_year": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The expiry year of the network token if active"
|
||||||
|
},
|
||||||
|
"card_last_four": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The last four digits of the card"
|
||||||
|
},
|
||||||
|
"token_last_four": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The last four digits of the network token"
|
||||||
|
},
|
||||||
|
"card_expiry": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The expiry date of the card in MM/YY format"
|
||||||
|
},
|
||||||
|
"payment_method_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The payment method ID that was checked",
|
||||||
|
"example": "12345_pm_019959146f92737389eb6927ce1eb7dc"
|
||||||
|
},
|
||||||
|
"customer_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The customer ID associated with the payment method",
|
||||||
|
"example": "12345_cus_0195dc62bb8e7312a44484536da76aef"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"NetworkTokenization": {
|
"NetworkTokenization": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "The network tokenization configuration for creating the payment method session",
|
"description": "The network tokenization configuration for creating the payment method session",
|
||||||
@ -26042,6 +26192,14 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"TokenStatus": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ACTIVE",
|
||||||
|
"SUSPENDED",
|
||||||
|
"DEACTIVATED"
|
||||||
|
]
|
||||||
|
},
|
||||||
"TokenType": {
|
"TokenType": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": [
|
"enum": [
|
||||||
|
|||||||
@ -492,3 +492,29 @@ impl From<PermissionScope> for ReconPermissionScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
Hash,
|
||||||
|
PartialEq,
|
||||||
|
ToSchema,
|
||||||
|
serde::Deserialize,
|
||||||
|
serde::Serialize,
|
||||||
|
strum::Display,
|
||||||
|
strum::EnumIter,
|
||||||
|
strum::EnumString,
|
||||||
|
)]
|
||||||
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
|
#[strum(serialize_all = "UPPERCASE")]
|
||||||
|
pub enum TokenStatus {
|
||||||
|
/// Indicates that the token is active and can be used for payments
|
||||||
|
Active,
|
||||||
|
/// Indicates that the token is suspended from network's end for some reason and can't be used for payments until it is re-activated
|
||||||
|
Suspended,
|
||||||
|
/// Indicates that the token is deactivated and further can't be used for payments
|
||||||
|
Deactivated,
|
||||||
|
}
|
||||||
|
|||||||
@ -3262,3 +3262,56 @@ pub struct AuthenticationDetails {
|
|||||||
#[schema(value_type = Option<ErrorDetails>)]
|
#[schema(value_type = Option<ErrorDetails>)]
|
||||||
pub error: Option<payments::ErrorDetails>,
|
pub error: Option<payments::ErrorDetails>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
#[derive(Debug, serde::Serialize, ToSchema)]
|
||||||
|
pub struct NetworkTokenStatusCheckSuccessResponse {
|
||||||
|
/// The status of the network token
|
||||||
|
#[schema(value_type = TokenStatus)]
|
||||||
|
pub status: api_enums::TokenStatus,
|
||||||
|
|
||||||
|
/// The expiry month of the network token if active
|
||||||
|
#[schema(value_type = String)]
|
||||||
|
pub token_expiry_month: masking::Secret<String>,
|
||||||
|
|
||||||
|
/// The expiry year of the network token if active
|
||||||
|
#[schema(value_type = String)]
|
||||||
|
pub token_expiry_year: masking::Secret<String>,
|
||||||
|
|
||||||
|
/// The last four digits of the card
|
||||||
|
pub card_last_four: String,
|
||||||
|
|
||||||
|
/// The last four digits of the network token
|
||||||
|
pub token_last_four: String,
|
||||||
|
|
||||||
|
/// The expiry date of the card in MM/YY format
|
||||||
|
pub card_expiry: String,
|
||||||
|
|
||||||
|
/// The payment method ID that was checked
|
||||||
|
#[schema(value_type = String, example = "12345_pm_019959146f92737389eb6927ce1eb7dc")]
|
||||||
|
pub payment_method_id: id_type::GlobalPaymentMethodId,
|
||||||
|
|
||||||
|
/// The customer ID associated with the payment method
|
||||||
|
#[schema(value_type = String, example = "12345_cus_0195dc62bb8e7312a44484536da76aef")]
|
||||||
|
pub customer_id: id_type::GlobalCustomerId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
impl common_utils::events::ApiEventMetric for NetworkTokenStatusCheckResponse {}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
#[derive(Debug, serde::Serialize, ToSchema)]
|
||||||
|
pub struct NetworkTokenStatusCheckFailureResponse {
|
||||||
|
/// Error message describing what went wrong
|
||||||
|
pub error_message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
#[derive(Debug, serde::Serialize, ToSchema)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
pub enum NetworkTokenStatusCheckResponse {
|
||||||
|
/// Successful network token status check response
|
||||||
|
SuccessResponse(NetworkTokenStatusCheckSuccessResponse),
|
||||||
|
/// Error response for network token status check
|
||||||
|
FailureResponse(NetworkTokenStatusCheckFailureResponse),
|
||||||
|
}
|
||||||
|
|||||||
@ -137,6 +137,7 @@ Never share your secret api keys. Keep them guarded and secure.
|
|||||||
routes::payment_method::payment_method_update_api,
|
routes::payment_method::payment_method_update_api,
|
||||||
routes::payment_method::payment_method_retrieve_api,
|
routes::payment_method::payment_method_retrieve_api,
|
||||||
routes::payment_method::payment_method_delete_api,
|
routes::payment_method::payment_method_delete_api,
|
||||||
|
routes::payment_method::network_token_status_check_api,
|
||||||
routes::payment_method::list_customer_payment_method_api,
|
routes::payment_method::list_customer_payment_method_api,
|
||||||
|
|
||||||
//Routes for payment method session
|
//Routes for payment method session
|
||||||
@ -273,6 +274,10 @@ Never share your secret api keys. Keep them guarded and secure.
|
|||||||
api_models::payment_methods::RequestPaymentMethodTypes,
|
api_models::payment_methods::RequestPaymentMethodTypes,
|
||||||
api_models::payment_methods::CardType,
|
api_models::payment_methods::CardType,
|
||||||
api_models::payment_methods::PaymentMethodListData,
|
api_models::payment_methods::PaymentMethodListData,
|
||||||
|
api_models::payment_methods::NetworkTokenStatusCheckResponse,
|
||||||
|
api_models::payment_methods::NetworkTokenStatusCheckSuccessResponse,
|
||||||
|
api_models::payment_methods::NetworkTokenStatusCheckFailureResponse,
|
||||||
|
api_models::enums::TokenStatus,
|
||||||
api_models::poll::PollResponse,
|
api_models::poll::PollResponse,
|
||||||
api_models::poll::PollStatus,
|
api_models::poll::PollStatus,
|
||||||
api_models::customers::CustomerResponse,
|
api_models::customers::CustomerResponse,
|
||||||
|
|||||||
@ -325,6 +325,26 @@ pub async fn payment_method_update_api() {}
|
|||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
pub async fn payment_method_delete_api() {}
|
pub async fn payment_method_delete_api() {}
|
||||||
|
|
||||||
|
/// Payment Method - Check Network Token Status
|
||||||
|
///
|
||||||
|
/// Check the status of a network token for a saved payment method
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/v2/payment-methods/{payment_method_id}/check-network-token-status",
|
||||||
|
params (
|
||||||
|
("payment_method_id" = String, Path, description = "The unique identifier for the Payment Method"),
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Network Token Status Retrieved", body = NetworkTokenStatusCheckResponse),
|
||||||
|
(status = 404, description = "Payment Method Not Found"),
|
||||||
|
),
|
||||||
|
tag = "Payment Methods",
|
||||||
|
operation_id = "Check Network Token Status",
|
||||||
|
security(("api_key" = []))
|
||||||
|
)]
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
pub async fn network_token_status_check_api() {}
|
||||||
|
|
||||||
/// Payment Method - List Customer Saved Payment Methods
|
/// Payment Method - List Customer Saved Payment Methods
|
||||||
///
|
///
|
||||||
/// List the payment methods saved for a customer
|
/// List the payment methods saved for a customer
|
||||||
|
|||||||
@ -3913,3 +3913,99 @@ async fn get_single_use_token_from_store(
|
|||||||
.change_context(errors::StorageError::KVError)
|
.change_context(errors::StorageError::KVError)
|
||||||
.attach_printable("Failed to get payment method token from redis")
|
.attach_printable("Failed to get payment method token from redis")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
async fn fetch_payment_method(
|
||||||
|
state: &SessionState,
|
||||||
|
merchant_context: &domain::MerchantContext,
|
||||||
|
payment_method_id: &id_type::GlobalPaymentMethodId,
|
||||||
|
) -> RouterResult<domain::PaymentMethod> {
|
||||||
|
let db = &state.store;
|
||||||
|
let key_manager_state = &state.into();
|
||||||
|
let merchant_account = merchant_context.get_merchant_account();
|
||||||
|
let key_store = merchant_context.get_merchant_key_store();
|
||||||
|
|
||||||
|
db.find_payment_method(
|
||||||
|
key_manager_state,
|
||||||
|
key_store,
|
||||||
|
payment_method_id,
|
||||||
|
merchant_account.storage_scheme,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)
|
||||||
|
.attach_printable("Payment method not found for network token status check")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
pub async fn check_network_token_status(
|
||||||
|
state: SessionState,
|
||||||
|
merchant_context: domain::MerchantContext,
|
||||||
|
payment_method_id: id_type::GlobalPaymentMethodId,
|
||||||
|
) -> RouterResponse<payment_methods::NetworkTokenStatusCheckResponse> {
|
||||||
|
// Retrieve the payment method from the database
|
||||||
|
let payment_method =
|
||||||
|
fetch_payment_method(&state, &merchant_context, &payment_method_id).await?;
|
||||||
|
|
||||||
|
// Call the network token status check function
|
||||||
|
let network_token_status_check_response = if payment_method.status
|
||||||
|
== common_enums::PaymentMethodStatus::Active
|
||||||
|
{
|
||||||
|
// Check if the payment method has network token data
|
||||||
|
when(
|
||||||
|
payment_method
|
||||||
|
.network_token_requestor_reference_id
|
||||||
|
.is_none(),
|
||||||
|
|| {
|
||||||
|
Err(errors::ApiErrorResponse::InvalidDataValue {
|
||||||
|
field_name: "payment_method_id",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
match network_tokenization::do_status_check_for_network_token(&state, &payment_method).await
|
||||||
|
{
|
||||||
|
Ok(network_token_details) => {
|
||||||
|
let status = match network_token_details.token_status {
|
||||||
|
pm_types::TokenStatus::Active => api_enums::TokenStatus::Active,
|
||||||
|
pm_types::TokenStatus::Suspended => api_enums::TokenStatus::Suspended,
|
||||||
|
pm_types::TokenStatus::Deactivated => api_enums::TokenStatus::Deactivated,
|
||||||
|
};
|
||||||
|
|
||||||
|
payment_methods::NetworkTokenStatusCheckResponse::SuccessResponse(
|
||||||
|
payment_methods::NetworkTokenStatusCheckSuccessResponse {
|
||||||
|
status,
|
||||||
|
token_expiry_month: network_token_details.token_expiry_month,
|
||||||
|
token_expiry_year: network_token_details.token_expiry_year,
|
||||||
|
card_last_four: network_token_details.card_last_4,
|
||||||
|
card_expiry: network_token_details.card_expiry,
|
||||||
|
token_last_four: network_token_details.token_last_4,
|
||||||
|
payment_method_id,
|
||||||
|
customer_id: payment_method.customer_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
let err_message = e.current_context().to_string();
|
||||||
|
logger::debug!("Network token status check failed: {:?}", e);
|
||||||
|
|
||||||
|
payment_methods::NetworkTokenStatusCheckResponse::FailureResponse(
|
||||||
|
payment_methods::NetworkTokenStatusCheckFailureResponse {
|
||||||
|
error_message: err_message,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let err_message = "Payment Method is not active".to_string();
|
||||||
|
logger::debug!("Payment Method is not active");
|
||||||
|
|
||||||
|
payment_methods::NetworkTokenStatusCheckResponse::FailureResponse(
|
||||||
|
payment_methods::NetworkTokenStatusCheckFailureResponse {
|
||||||
|
error_message: err_message,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
Ok(services::ApplicationResponse::Json(
|
||||||
|
network_token_status_check_response,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|||||||
@ -778,24 +778,125 @@ pub async fn check_token_status_with_tokenization_service(
|
|||||||
.parse_struct("Delete Network Tokenization Response")
|
.parse_struct("Delete Network Tokenization Response")
|
||||||
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
|
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
match check_token_status_response.payload.token_status {
|
match check_token_status_response.token_status {
|
||||||
pm_types::TokenStatus::Active => Ok((
|
pm_types::TokenStatus::Active => Ok((
|
||||||
Some(check_token_status_response.payload.token_expiry_month),
|
Some(check_token_status_response.token_expiry_month),
|
||||||
Some(check_token_status_response.payload.token_expiry_year),
|
Some(check_token_status_response.token_expiry_year),
|
||||||
)),
|
)),
|
||||||
pm_types::TokenStatus::Inactive => Ok((None, None)),
|
_ => Ok((None, None)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "v2")]
|
#[cfg(feature = "v2")]
|
||||||
pub async fn check_token_status_with_tokenization_service(
|
pub async fn check_token_status_with_tokenization_service(
|
||||||
_state: &routes::SessionState,
|
state: &routes::SessionState,
|
||||||
_customer_id: &id_type::GlobalCustomerId,
|
customer_id: &id_type::GlobalCustomerId,
|
||||||
_network_token_requestor_reference_id: String,
|
network_token_requestor_reference_id: String,
|
||||||
_tokenization_service: &settings::NetworkTokenizationService,
|
tokenization_service: &settings::NetworkTokenizationService,
|
||||||
) -> CustomResult<(Option<Secret<String>>, Option<Secret<String>>), errors::NetworkTokenizationError>
|
) -> CustomResult<pm_types::CheckTokenStatusResponse, errors::NetworkTokenizationError> {
|
||||||
{
|
let mut request = services::Request::new(
|
||||||
todo!()
|
services::Method::Post,
|
||||||
|
tokenization_service.check_token_status_url.as_str(),
|
||||||
|
);
|
||||||
|
let payload = pm_types::CheckTokenStatus {
|
||||||
|
card_reference: network_token_requestor_reference_id,
|
||||||
|
customer_id: customer_id.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
request.add_header(headers::CONTENT_TYPE, "application/json".into());
|
||||||
|
request.add_header(
|
||||||
|
headers::AUTHORIZATION,
|
||||||
|
tokenization_service
|
||||||
|
.token_service_api_key
|
||||||
|
.clone()
|
||||||
|
.peek()
|
||||||
|
.clone()
|
||||||
|
.into_masked(),
|
||||||
|
);
|
||||||
|
request.add_default_headers();
|
||||||
|
request.set_body(RequestContent::Json(Box::new(payload)));
|
||||||
|
|
||||||
|
// Send the request using `call_connector_api`
|
||||||
|
let response = services::call_connector_api(state, request, "Check Network token Status")
|
||||||
|
.await
|
||||||
|
.change_context(errors::NetworkTokenizationError::ApiError);
|
||||||
|
let res = response
|
||||||
|
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)
|
||||||
|
.attach_printable("Error while receiving response")
|
||||||
|
.and_then(|inner| match inner {
|
||||||
|
Err(err_res) => {
|
||||||
|
let parsed_error: pm_types::NetworkTokenErrorResponse = err_res
|
||||||
|
.response
|
||||||
|
.parse_struct("Network Tokenization Error Response")
|
||||||
|
.change_context(
|
||||||
|
errors::NetworkTokenizationError::ResponseDeserializationFailed,
|
||||||
|
)?;
|
||||||
|
logger::error!(
|
||||||
|
error_code = %parsed_error.error_info.code,
|
||||||
|
developer_message = %parsed_error.error_info.developer_message,
|
||||||
|
"Network tokenization error: {}",
|
||||||
|
parsed_error.error_message
|
||||||
|
);
|
||||||
|
Err(errors::NetworkTokenizationError::ResponseDeserializationFailed)
|
||||||
|
.attach_printable(format!("Response Deserialization Failed: {err_res:?}"))
|
||||||
|
}
|
||||||
|
Ok(res) => Ok(res),
|
||||||
|
})
|
||||||
|
.inspect_err(|err| {
|
||||||
|
logger::error!("Error while deserializing response: {:?}", err);
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let check_token_status_response: pm_types::CheckTokenStatusResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("CheckTokenStatusResponse")
|
||||||
|
.change_context(errors::NetworkTokenizationError::ResponseDeserializationFailed)?;
|
||||||
|
|
||||||
|
Ok(check_token_status_response)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
pub async fn do_status_check_for_network_token(
|
||||||
|
state: &routes::SessionState,
|
||||||
|
payment_method_info: &domain::PaymentMethod,
|
||||||
|
) -> CustomResult<pm_types::CheckTokenStatusResponse, errors::NetworkTokenizationError> {
|
||||||
|
let network_token_requestor_reference_id = payment_method_info
|
||||||
|
.network_token_requestor_reference_id
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
if let Some(ref_id) = network_token_requestor_reference_id {
|
||||||
|
if let Some(network_tokenization_service) = &state.conf.network_tokenization_service {
|
||||||
|
let network_token_details = record_operation_time(
|
||||||
|
async {
|
||||||
|
check_token_status_with_tokenization_service(
|
||||||
|
state,
|
||||||
|
&payment_method_info.customer_id,
|
||||||
|
ref_id,
|
||||||
|
network_tokenization_service.get_inner(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.inspect_err(
|
||||||
|
|e| logger::error!(error=?e, "Error while fetching token from tokenization service")
|
||||||
|
)
|
||||||
|
.attach_printable(
|
||||||
|
"Check network token status with tokenization service failed",
|
||||||
|
)
|
||||||
|
},
|
||||||
|
&metrics::CHECK_NETWORK_TOKEN_STATUS_TIME,
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(network_token_details)
|
||||||
|
} else {
|
||||||
|
Err(errors::NetworkTokenizationError::NetworkTokenizationServiceNotConfigured)
|
||||||
|
.attach_printable("Network Tokenization Service not configured")
|
||||||
|
.inspect_err(|_| {
|
||||||
|
logger::error!("Network Tokenization Service not configured");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(errors::NetworkTokenizationError::FetchNetworkTokenFailed)
|
||||||
|
.attach_printable("Check network token status failed")?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "v1")]
|
#[cfg(feature = "v1")]
|
||||||
|
|||||||
@ -1455,6 +1455,10 @@ impl PaymentMethods {
|
|||||||
.service(
|
.service(
|
||||||
web::resource("/create-intent")
|
web::resource("/create-intent")
|
||||||
.route(web::post().to(payment_methods::create_payment_method_intent_api)),
|
.route(web::post().to(payment_methods::create_payment_method_intent_api)),
|
||||||
|
)
|
||||||
|
.service(
|
||||||
|
web::resource("/{payment_method_id}/check-network-token-status")
|
||||||
|
.route(web::get().to(payment_methods::network_token_status_check_api)),
|
||||||
);
|
);
|
||||||
|
|
||||||
route = route.service(
|
route = route.service(
|
||||||
|
|||||||
@ -126,6 +126,7 @@ impl From<Flow> for ApiIdentifier {
|
|||||||
| Flow::PaymentMethodsRetrieve
|
| Flow::PaymentMethodsRetrieve
|
||||||
| Flow::PaymentMethodsUpdate
|
| Flow::PaymentMethodsUpdate
|
||||||
| Flow::PaymentMethodsDelete
|
| Flow::PaymentMethodsDelete
|
||||||
|
| Flow::NetworkTokenStatusCheck
|
||||||
| Flow::PaymentMethodCollectLink
|
| Flow::PaymentMethodCollectLink
|
||||||
| Flow::ValidatePaymentMethod
|
| Flow::ValidatePaymentMethod
|
||||||
| Flow::ListCountriesCurrencies
|
| Flow::ListCountriesCurrencies
|
||||||
|
|||||||
@ -1559,3 +1559,37 @@ pub async fn payment_method_session_delete_saved_payment_method(
|
|||||||
))
|
))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "v2")]
|
||||||
|
#[instrument(skip_all, fields(flow = ?Flow::NetworkTokenStatusCheck))]
|
||||||
|
pub async fn network_token_status_check_api(
|
||||||
|
state: web::Data<AppState>,
|
||||||
|
req: HttpRequest,
|
||||||
|
path: web::Path<id_type::GlobalPaymentMethodId>,
|
||||||
|
) -> HttpResponse {
|
||||||
|
let flow = Flow::NetworkTokenStatusCheck;
|
||||||
|
let payment_method_id = path.into_inner();
|
||||||
|
|
||||||
|
Box::pin(api::server_wrap(
|
||||||
|
flow,
|
||||||
|
state,
|
||||||
|
&req,
|
||||||
|
payment_method_id,
|
||||||
|
|state, auth: auth::AuthenticationData, payment_method_id, _| {
|
||||||
|
let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
|
||||||
|
domain::Context(auth.merchant_account, auth.key_store),
|
||||||
|
));
|
||||||
|
payment_methods_routes::check_network_token_status(
|
||||||
|
state,
|
||||||
|
merchant_context,
|
||||||
|
payment_method_id,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
&auth::V2ApiKeyAuth {
|
||||||
|
is_connected_allowed: false,
|
||||||
|
is_platform_allowed: false,
|
||||||
|
},
|
||||||
|
api_locking::LockAction::NotApplicable,
|
||||||
|
))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|||||||
@ -344,24 +344,23 @@ pub struct CheckTokenStatus {
|
|||||||
pub customer_id: id_type::GlobalCustomerId,
|
pub customer_id: id_type::GlobalCustomerId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
#[serde(rename_all = "UPPERCASE")]
|
#[serde(rename_all = "UPPERCASE")]
|
||||||
pub enum TokenStatus {
|
pub enum TokenStatus {
|
||||||
Active,
|
Active,
|
||||||
Inactive,
|
Suspended,
|
||||||
|
Deactivated,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct CheckTokenStatusResponsePayload {
|
pub struct CheckTokenStatusResponse {
|
||||||
|
pub token_status: TokenStatus,
|
||||||
pub token_expiry_month: Secret<String>,
|
pub token_expiry_month: Secret<String>,
|
||||||
pub token_expiry_year: Secret<String>,
|
pub token_expiry_year: Secret<String>,
|
||||||
pub token_status: TokenStatus,
|
pub card_last_4: String,
|
||||||
}
|
pub card_expiry: String,
|
||||||
|
pub token_last_4: String,
|
||||||
#[derive(Debug, Deserialize)]
|
|
||||||
pub struct CheckTokenStatusResponse {
|
|
||||||
pub payload: CheckTokenStatusResponsePayload,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
|||||||
@ -140,6 +140,8 @@ pub enum Flow {
|
|||||||
PaymentMethodsUpdate,
|
PaymentMethodsUpdate,
|
||||||
/// Payment methods delete flow.
|
/// Payment methods delete flow.
|
||||||
PaymentMethodsDelete,
|
PaymentMethodsDelete,
|
||||||
|
/// Network token status check flow.
|
||||||
|
NetworkTokenStatusCheck,
|
||||||
/// Default Payment method flow.
|
/// Default Payment method flow.
|
||||||
DefaultPaymentMethodsSet,
|
DefaultPaymentMethodsSet,
|
||||||
/// Payments create flow.
|
/// Payments create flow.
|
||||||
|
|||||||
Reference in New Issue
Block a user