mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 01:57:45 +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" | ||||
|                     ] | ||||
|                   }, | ||||
|                   { | ||||
|                     "group": "Network Tokenization", | ||||
|                     "pages": [ | ||||
|                       "v2/payment-methods/check-network-token-status" | ||||
|                     ] | ||||
|                   }, | ||||
|                   { | ||||
|                     "group": "Payment Method Session", | ||||
|                     "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": { | ||||
|       "get": { | ||||
|         "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": { | ||||
|         "type": "object", | ||||
|         "description": "The network tokenization configuration for creating the payment method session", | ||||
| @ -26042,6 +26192,14 @@ | ||||
|           } | ||||
|         ] | ||||
|       }, | ||||
|       "TokenStatus": { | ||||
|         "type": "string", | ||||
|         "enum": [ | ||||
|           "ACTIVE", | ||||
|           "SUSPENDED", | ||||
|           "DEACTIVATED" | ||||
|         ] | ||||
|       }, | ||||
|       "TokenType": { | ||||
|         "type": "string", | ||||
|         "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>)] | ||||
|     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_retrieve_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 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::CardType, | ||||
|         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::PollStatus, | ||||
|         api_models::customers::CustomerResponse, | ||||
|  | ||||
| @ -325,6 +325,26 @@ pub async fn payment_method_update_api() {} | ||||
| #[cfg(feature = "v2")] | ||||
| 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 | ||||
| /// | ||||
| /// 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) | ||||
|         .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") | ||||
|         .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(( | ||||
|             Some(check_token_status_response.payload.token_expiry_month), | ||||
|             Some(check_token_status_response.payload.token_expiry_year), | ||||
|             Some(check_token_status_response.token_expiry_month), | ||||
|             Some(check_token_status_response.token_expiry_year), | ||||
|         )), | ||||
|         pm_types::TokenStatus::Inactive => Ok((None, None)), | ||||
|         _ => Ok((None, None)), | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "v2")] | ||||
| pub async fn check_token_status_with_tokenization_service( | ||||
|     _state: &routes::SessionState, | ||||
|     _customer_id: &id_type::GlobalCustomerId, | ||||
|     _network_token_requestor_reference_id: String, | ||||
|     _tokenization_service: &settings::NetworkTokenizationService, | ||||
| ) -> CustomResult<(Option<Secret<String>>, Option<Secret<String>>), errors::NetworkTokenizationError> | ||||
| { | ||||
|     todo!() | ||||
|     state: &routes::SessionState, | ||||
|     customer_id: &id_type::GlobalCustomerId, | ||||
|     network_token_requestor_reference_id: String, | ||||
|     tokenization_service: &settings::NetworkTokenizationService, | ||||
| ) -> CustomResult<pm_types::CheckTokenStatusResponse, errors::NetworkTokenizationError> { | ||||
|     let mut request = services::Request::new( | ||||
|         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")] | ||||
|  | ||||
| @ -1455,6 +1455,10 @@ impl PaymentMethods { | ||||
|                 .service( | ||||
|                     web::resource("/create-intent") | ||||
|                         .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( | ||||
|  | ||||
| @ -126,6 +126,7 @@ impl From<Flow> for ApiIdentifier { | ||||
|             | Flow::PaymentMethodsRetrieve | ||||
|             | Flow::PaymentMethodsUpdate | ||||
|             | Flow::PaymentMethodsDelete | ||||
|             | Flow::NetworkTokenStatusCheck | ||||
|             | Flow::PaymentMethodCollectLink | ||||
|             | Flow::ValidatePaymentMethod | ||||
|             | Flow::ListCountriesCurrencies | ||||
|  | ||||
| @ -1559,3 +1559,37 @@ pub async fn payment_method_session_delete_saved_payment_method( | ||||
|     )) | ||||
|     .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, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| #[derive(Debug, Deserialize, Clone)] | ||||
| #[serde(rename_all = "UPPERCASE")] | ||||
| pub enum TokenStatus { | ||||
|     Active, | ||||
|     Inactive, | ||||
|     Suspended, | ||||
|     Deactivated, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct CheckTokenStatusResponsePayload { | ||||
| pub struct CheckTokenStatusResponse { | ||||
|     pub token_status: TokenStatus, | ||||
|     pub token_expiry_month: Secret<String>, | ||||
|     pub token_expiry_year: Secret<String>, | ||||
|     pub token_status: TokenStatus, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Deserialize)] | ||||
| pub struct CheckTokenStatusResponse { | ||||
|     pub payload: CheckTokenStatusResponsePayload, | ||||
|     pub card_last_4: String, | ||||
|     pub card_expiry: String, | ||||
|     pub token_last_4: String, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Deserialize, Serialize)] | ||||
|  | ||||
| @ -140,6 +140,8 @@ pub enum Flow { | ||||
|     PaymentMethodsUpdate, | ||||
|     /// Payment methods delete flow. | ||||
|     PaymentMethodsDelete, | ||||
|     /// Network token status check flow. | ||||
|     NetworkTokenStatusCheck, | ||||
|     /// Default Payment method flow. | ||||
|     DefaultPaymentMethodsSet, | ||||
|     /// Payments create flow. | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Sagnik Mitra
					Sagnik Mitra