feat(list_pm): allow client secret authentication for list payment method api (#126)

This commit is contained in:
Nishant Joshi
2022-12-13 14:03:33 +05:30
committed by GitHub
parent 3acde2603b
commit 6bf9904867
9 changed files with 268 additions and 121 deletions

View File

@ -1,11 +1,9 @@
use common_utils::pii;
use masking::Secret;
use serde::{Deserialize, Serialize};
use time::PrimitiveDateTime;
use serde::de;
use crate::enums as api_enums;
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct CreatePaymentMethod {
pub merchant_id: Option<String>,
@ -18,16 +16,16 @@ pub struct CreatePaymentMethod {
pub customer_id: Option<String>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
#[serde(deny_unknown_fields)]
pub struct CardDetail {
pub card_number: Secret<String, pii::CardNumber>,
pub card_exp_month: Secret<String>,
pub card_exp_year: Secret<String>,
pub card_holder_name: Option<Secret<String>>,
pub card_number: masking::Secret<String, pii::CardNumber>,
pub card_exp_month: masking::Secret<String>,
pub card_exp_year: masking::Secret<String>,
pub card_holder_name: Option<masking::Secret<String>>,
}
#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, serde::Deserialize, serde::Serialize)]
pub struct PaymentMethodResponse {
pub payment_method_id: String,
pub payment_method: api_enums::PaymentMethodType,
@ -45,27 +43,28 @@ pub struct PaymentMethodResponse {
pub payment_experience: Option<Vec<String>>, //TODO change it to enum
pub metadata: Option<serde_json::Value>,
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
pub created: Option<PrimitiveDateTime>,
pub created: Option<time::PrimitiveDateTime>,
}
#[derive(Debug, Deserialize, Serialize, Clone)]
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CardDetailFromLocker {
pub scheme: Option<String>,
pub issuer_country: Option<String>,
pub last4_digits: Option<String>,
#[serde(skip)]
pub card_number: Option<Secret<String, pii::CardNumber>>,
pub expiry_month: Option<Secret<String>>,
pub expiry_year: Option<Secret<String>>,
pub card_token: Option<Secret<String>>,
pub card_holder_name: Option<Secret<String>>,
pub card_fingerprint: Option<Secret<String>>,
pub card_number: Option<masking::Secret<String, pii::CardNumber>>,
pub expiry_month: Option<masking::Secret<String>>,
pub expiry_year: Option<masking::Secret<String>>,
pub card_token: Option<masking::Secret<String>>,
pub card_holder_name: Option<masking::Secret<String>>,
pub card_fingerprint: Option<masking::Secret<String>>,
}
//List Payment Method
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, serde::Serialize, Default)]
#[serde(deny_unknown_fields)]
pub struct ListPaymentMethodRequest {
pub client_secret: Option<String>,
pub accepted_countries: Option<Vec<String>>,
pub accepted_currencies: Option<Vec<api_enums::Currency>>,
pub amount: Option<i32>,
@ -73,7 +72,65 @@ pub struct ListPaymentMethodRequest {
pub installment_payment_enabled: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
impl<'de> serde::Deserialize<'de> for ListPaymentMethodRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
struct FieldVisitor;
impl<'de> de::Visitor<'de> for FieldVisitor {
type Value = ListPaymentMethodRequest;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("Failed while deserializing as map")
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: de::MapAccess<'de>,
{
let mut output = ListPaymentMethodRequest::default();
while let Some(key) = map.next_key()? {
match key {
"client_secret" => {
output.client_secret = Some(map.next_value()?);
}
"accepted_countries" => match output.accepted_countries.as_mut() {
Some(inner) => inner.push(map.next_value()?),
None => {
output.accepted_countries = Some(vec![map.next_value()?]);
}
},
"accepted_currencies" => match output.accepted_currencies.as_mut() {
Some(inner) => inner.push(map.next_value()?),
None => {
output.accepted_currencies = Some(vec![map.next_value()?]);
}
},
"amount" => {
output.amount = Some(map.next_value()?);
}
"recurring_enabled" => {
output.recurring_enabled = Some(map.next_value()?);
}
"installment_payment_enabled" => {
output.installment_payment_enabled = Some(map.next_value()?);
}
_ => {}
}
}
Ok(output)
}
}
deserializer.deserialize_identifier(FieldVisitor)
}
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct ListPaymentMethodResponse {
pub payment_method: api_enums::PaymentMethodType,
pub payment_method_types: Option<Vec<api_enums::PaymentMethodSubType>>,
@ -89,19 +146,19 @@ pub struct ListPaymentMethodResponse {
pub payment_experience: Option<Vec<String>>, //TODO change it to enum
}
#[derive(Debug, Serialize)]
#[derive(Debug, serde::Serialize)]
pub struct ListCustomerPaymentMethodsResponse {
pub enabled_payment_methods: Vec<ListPaymentMethodResponse>,
pub customer_payment_methods: Vec<CustomerPaymentMethod>,
}
#[derive(Debug, Serialize)]
#[derive(Debug, serde::Serialize)]
pub struct DeletePaymentMethodResponse {
pub payment_method_id: String,
pub deleted: bool,
}
#[derive(Debug, Serialize)]
#[derive(Debug, serde::Serialize)]
pub struct CustomerPaymentMethod {
pub payment_token: String,
pub customer_id: String,
@ -120,23 +177,23 @@ pub struct CustomerPaymentMethod {
pub card: Option<CardDetailFromLocker>,
pub metadata: Option<serde_json::Value>,
#[serde(default, with = "common_utils::custom_serde::iso8601::option")]
pub created: Option<PrimitiveDateTime>,
pub created: Option<time::PrimitiveDateTime>,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct PaymentMethodId {
pub payment_method_id: String,
}
//------------------------------------------------TokenizeService------------------------------------------------
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizePayloadEncrypted {
pub payload: String,
pub key_id: String,
pub version: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct TokenizePayloadRequest {
pub value1: String,
pub value2: String,
@ -144,30 +201,30 @@ pub struct TokenizePayloadRequest {
pub service_name: String,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct GetTokenizePayloadRequest {
pub lookup_key: String,
pub get_value2: bool,
}
#[derive(Debug, Serialize)]
#[derive(Debug, serde::Serialize)]
pub struct DeleteTokenizeByTokenRequest {
pub lookup_key: String,
}
#[derive(Debug, Serialize)] //FIXME yet to be implemented
#[derive(Debug, serde::Serialize)] //FIXME yet to be implemented
pub struct DeleteTokenizeByDateRequest {
pub buffer_minutes: i32,
pub service_name: String,
pub max_rows: i32,
}
#[derive(Debug, Deserialize)]
#[derive(Debug, serde::Deserialize)]
pub struct GetTokenizePayloadResponse {
pub lookup_key: String,
pub get_value2: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardValue1 {
pub card_number: String,
@ -179,7 +236,7 @@ pub struct TokenizedCardValue1 {
pub card_token: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TokenizedCardValue2 {
pub card_security_code: Option<String>,

View File

@ -1,20 +1,18 @@
use std::{collections::HashSet, hash::Hash};
use std::collections;
use error_stack::{report, ResultExt};
use router_env::{tracing, tracing::instrument};
use serde_json::Value;
use uuid::Uuid;
use crate::{
configs::settings::Keys,
configs::settings,
core::{
errors::{self, CustomResult, RouterResponse, RouterResult, StorageErrorExt},
errors::{self, StorageErrorExt},
payment_methods::transformers as payment_methods,
payments::helpers,
},
db::StorageInterface,
db,
pii::prelude::*,
routes::AppState,
services,
routes, services,
types::{
api::{self, CreatePaymentMethodExt},
storage::{self, enums},
@ -25,12 +23,12 @@ use crate::{
#[instrument(skip_all)]
pub async fn create_payment_method(
db: &dyn StorageInterface,
db: &dyn db::StorageInterface,
req: &api::CreatePaymentMethod,
customer_id: String,
payment_method_id: String,
merchant_id: &str,
) -> CustomResult<storage::PaymentMethod, errors::StorageError> {
) -> errors::CustomResult<storage::PaymentMethod, errors::StorageError> {
let response = db
.insert_payment_method(storage::PaymentMethodNew {
customer_id,
@ -48,10 +46,10 @@ pub async fn create_payment_method(
#[instrument(skip_all)]
pub async fn add_payment_method(
state: &AppState,
state: &routes::AppState,
req: api::CreatePaymentMethod,
merchant_id: String,
) -> RouterResponse<api::PaymentMethodResponse> {
) -> errors::RouterResponse<api::PaymentMethodResponse> {
req.validate()?;
let customer_id = req.customer_id.clone().get_required_value("customer_id")?;
@ -92,12 +90,12 @@ pub async fn add_payment_method(
#[instrument(skip_all)]
pub async fn add_card(
state: &AppState,
state: &routes::AppState,
req: api::CreatePaymentMethod,
card: api::CardDetail,
customer_id: String,
merchant_id: &str,
) -> CustomResult<api::PaymentMethodResponse, errors::CardVaultError> {
) -> errors::CustomResult<api::PaymentMethodResponse, errors::CardVaultError> {
let locker = &state.conf.locker;
let db = &*state.store;
let request = payment_methods::mk_add_card_request(locker, &card, &customer_id, &req)?;
@ -118,7 +116,7 @@ pub async fn add_card(
}?;
response
} else {
let card_id = Uuid::new_v4().to_string();
let card_id = uuid::Uuid::new_v4().to_string();
mock_add_card(db, &card_id, &card).await?
};
@ -137,15 +135,15 @@ pub async fn add_card(
#[instrument(skip_all)]
pub async fn mock_add_card(
db: &dyn StorageInterface,
db: &dyn db::StorageInterface,
card_id: &str,
card: &api::CardDetail,
) -> CustomResult<payment_methods::AddCardResponse, errors::CardVaultError> {
) -> errors::CustomResult<payment_methods::AddCardResponse, errors::CardVaultError> {
let locker_mock_up = storage::LockerMockUpNew {
card_id: card_id.to_string(),
external_id: Uuid::new_v4().to_string(),
card_fingerprint: Uuid::new_v4().to_string(),
card_global_fingerprint: Uuid::new_v4().to_string(),
external_id: uuid::Uuid::new_v4().to_string(),
card_fingerprint: uuid::Uuid::new_v4().to_string(),
card_global_fingerprint: uuid::Uuid::new_v4().to_string(),
merchant_id: "mm01".to_string(),
card_number: card.card_number.peek().to_string(),
card_exp_year: card.card_exp_year.peek().to_string(),
@ -174,9 +172,9 @@ pub async fn mock_add_card(
#[instrument(skip_all)]
pub async fn mock_get_card<'a>(
db: &dyn StorageInterface,
db: &dyn db::StorageInterface,
card_id: &'a str,
) -> CustomResult<payment_methods::GetCardResponse, errors::CardVaultError> {
) -> errors::CustomResult<payment_methods::GetCardResponse, errors::CardVaultError> {
let locker_mock_up = db
.find_locker_by_card_id(card_id)
.await
@ -202,10 +200,10 @@ pub async fn mock_get_card<'a>(
#[instrument(skip_all)]
pub async fn get_card_from_legacy_locker<'a>(
state: &'a AppState,
state: &'a routes::AppState,
merchant_id: &'a str,
card_id: &'a str,
) -> RouterResult<payment_methods::GetCardResponse> {
) -> errors::RouterResult<payment_methods::GetCardResponse> {
let locker = &state.conf.locker;
let request = payment_methods::mk_get_card_request(locker, merchant_id, card_id)
.change_context(errors::ApiErrorResponse::InternalServerError)
@ -236,10 +234,10 @@ pub async fn get_card_from_legacy_locker<'a>(
#[instrument(skip_all)]
pub async fn delete_card<'a>(
state: &'a AppState,
state: &'a routes::AppState,
merchant_id: &'a str,
card_id: &'a str,
) -> RouterResult<payment_methods::DeleteCardResponse> {
) -> errors::RouterResult<payment_methods::DeleteCardResponse> {
let request = payment_methods::mk_delete_card_request(&state.conf.locker, merchant_id, card_id)
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Making Delete card request Failed")?;
@ -255,10 +253,18 @@ pub async fn delete_card<'a>(
}
pub async fn list_payment_methods(
db: &dyn StorageInterface,
db: &dyn db::StorageInterface,
merchant_account: storage::MerchantAccount,
mut req: api::ListPaymentMethodRequest,
) -> RouterResponse<Vec<api::ListPaymentMethodResponse>> {
) -> errors::RouterResponse<Vec<api::ListPaymentMethodResponse>> {
helpers::verify_client_secret(
db,
merchant_account.storage_scheme,
req.client_secret.clone(),
&merchant_account.merchant_id,
)
.await?;
let all_mcas = db
.find_merchant_connector_account_by_merchant_id_list(&merchant_account.merchant_id)
.await
@ -283,7 +289,7 @@ pub async fn list_payment_methods(
}
fn filter_payment_methods(
payment_methods: Vec<Value>,
payment_methods: Vec<serde_json::Value>,
req: &mut api::ListPaymentMethodRequest,
resp: &mut Vec<api::ListPaymentMethodResponse>,
) {
@ -325,14 +331,14 @@ fn filter_payment_methods(
}
}
fn filter_accepted_enum_based<T: Eq + Hash + Clone>(
fn filter_accepted_enum_based<T: Eq + std::hash::Hash + Clone>(
left: &Option<Vec<T>>,
right: &Option<Vec<T>>,
) -> (Option<Vec<T>>, Option<Vec<T>>, bool) {
match (left, right) {
(Some(ref l), Some(ref r)) => {
let a: HashSet<&T> = HashSet::from_iter(l.iter());
let b: HashSet<&T> = HashSet::from_iter(r.iter());
let a: collections::HashSet<&T> = collections::HashSet::from_iter(l.iter());
let b: collections::HashSet<&T> = collections::HashSet::from_iter(r.iter());
let y: Vec<T> = a.intersection(&b).map(|&i| i.to_owned()).collect();
(Some(y), Some(r.to_vec()), true)
@ -381,10 +387,10 @@ fn filter_installment_based(
}
pub async fn list_customer_payment_method(
state: &AppState,
state: &routes::AppState,
merchant_account: storage::MerchantAccount,
customer_id: &str,
) -> RouterResponse<api::ListCustomerPaymentMethodsResponse> {
) -> errors::RouterResponse<api::ListCustomerPaymentMethodsResponse> {
let db = &*state.store;
let all_mcas = db
.find_merchant_connector_account_by_merchant_id_list(&merchant_account.merchant_id)
@ -427,7 +433,7 @@ pub async fn list_customer_payment_method(
}
let mut vec = Vec::new();
for pm in resp.into_iter() {
let payment_token = Uuid::new_v4().to_string();
let payment_token = uuid::Uuid::new_v4().to_string();
let card = if pm.payment_method == enums::PaymentMethodType::Card {
Some(get_lookup_key_from_locker(state, &payment_token, &pm).await?)
} else {
@ -462,10 +468,10 @@ pub async fn list_customer_payment_method(
}
pub async fn get_lookup_key_from_locker(
state: &AppState,
state: &routes::AppState,
payment_token: &str,
pm: &storage::PaymentMethod,
) -> RouterResult<api::CardDetailFromLocker> {
) -> errors::RouterResult<api::CardDetailFromLocker> {
let get_card_resp = get_card_from_legacy_locker(
state,
pm.merchant_id.as_str(),
@ -487,10 +493,10 @@ pub struct BasiliskCardSupport;
#[cfg(not(feature = "basilisk"))]
impl BasiliskCardSupport {
async fn create_payment_method_data_in_locker(
state: &AppState,
state: &routes::AppState,
payment_token: &str,
card: api::CardDetailFromLocker,
) -> RouterResult<api::CardDetailFromLocker> {
) -> errors::RouterResult<api::CardDetailFromLocker> {
let card_number = card
.card_number
.clone()
@ -530,10 +536,10 @@ impl BasiliskCardSupport {
impl BasiliskCardSupport {
#[instrument(skip_all)]
async fn create_payment_method_data_in_locker(
state: &AppState,
state: &routes::AppState,
payment_token: &str,
card: api::CardDetailFromLocker,
) -> RouterResult<api::CardDetailFromLocker> {
) -> errors::RouterResult<api::CardDetailFromLocker> {
let card_number = card
.card_number
.clone()
@ -579,9 +585,9 @@ impl BasiliskCardSupport {
}
pub async fn get_card_info_value(
keys: &Keys,
keys: &settings::Keys,
card_info: String,
) -> RouterResult<serde_json::Value> {
) -> errors::RouterResult<serde_json::Value> {
let key = services::KeyHandler::get_encryption_key(keys)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
@ -593,9 +599,9 @@ pub async fn get_card_info_value(
}
pub async fn get_card_info_from_value(
keys: &Keys,
keys: &settings::Keys,
card_info: serde_json::Value,
) -> RouterResult<String> {
) -> errors::RouterResult<String> {
let key = services::KeyHandler::get_encryption_key(keys)
.await
.change_context(errors::ApiErrorResponse::InternalServerError)?;
@ -608,9 +614,9 @@ pub async fn get_card_info_from_value(
#[instrument(skip_all)]
pub async fn retrieve_payment_method(
state: &AppState,
state: &routes::AppState,
pm: api::PaymentMethodId,
) -> RouterResponse<api::PaymentMethodResponse> {
) -> errors::RouterResponse<api::PaymentMethodResponse> {
let db = &*state.store;
let pm = db
.find_payment_method(&pm.payment_method_id)
@ -644,10 +650,10 @@ pub async fn retrieve_payment_method(
#[instrument(skip_all)]
pub async fn delete_payment_method(
state: &AppState,
state: &routes::AppState,
merchant_account: storage::MerchantAccount,
pm: api::PaymentMethodId,
) -> RouterResponse<api::DeletePaymentMethodResponse> {
) -> errors::RouterResponse<api::DeletePaymentMethodResponse> {
let pm = state
.store
.delete_payment_method_by_merchant_id_payment_method_id(
@ -678,11 +684,11 @@ pub async fn delete_payment_method(
//------------------------------------------------TokenizeService------------------------------------------------
pub async fn create_tokenize(
state: &AppState,
state: &routes::AppState,
value1: String,
value2: Option<String>,
lookup_key: String,
) -> RouterResult<String> {
) -> errors::RouterResult<String> {
let payload_to_be_encrypted = api::TokenizePayloadRequest {
value1,
value2: value2.unwrap_or_default(),
@ -738,10 +744,10 @@ pub async fn create_tokenize(
}
pub async fn get_tokenized_data(
state: &AppState,
state: &routes::AppState,
lookup_key: &str,
should_get_value2: bool,
) -> RouterResult<api::TokenizePayloadRequest> {
) -> errors::RouterResult<api::TokenizePayloadRequest> {
let payload_to_be_encrypted = api::GetTokenizePayloadRequest {
lookup_key: lookup_key.to_string(),
get_value2: should_get_value2,
@ -792,7 +798,10 @@ pub async fn get_tokenized_data(
}
}
pub async fn delete_tokenized_data(state: &AppState, lookup_key: &str) -> RouterResult<String> {
pub async fn delete_tokenized_data(
state: &routes::AppState,
lookup_key: &str,
) -> errors::RouterResult<String> {
let payload_to_be_encrypted = api::DeleteTokenizeByTokenRequest {
lookup_key: lookup_key.to_string(),
};

View File

@ -16,7 +16,6 @@ pub use self::operations::{
};
use self::{
flows::{ConstructFlowSpecificData, Feature},
helpers::{filter_by_constraints, validate_payment_list_request},
operations::{BoxedOperation, Operation},
};
use super::errors::StorageErrorExt;
@ -573,10 +572,10 @@ pub async fn list_payments(
merchant: storage::MerchantAccount,
constraints: api::PaymentListConstraints,
) -> RouterResponse<api::PaymentListResponse> {
validate_payment_list_request(&constraints)?;
helpers::validate_payment_list_request(&constraints)?;
let merchant_id = &merchant.merchant_id;
let payment_intent =
filter_by_constraints(db, &constraints, merchant_id, merchant.storage_scheme)
helpers::filter_by_constraints(db, &constraints, merchant_id, merchant.storage_scheme)
.await
.map_err(|err| err.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound))?;

View File

@ -531,14 +531,17 @@ pub(crate) async fn call_payment_method(
}
}
pub(crate) fn client_secret_auth(
payload: api::PaymentsRequest,
pub(crate) fn client_secret_auth<P>(
payload: P,
auth_type: &services::api::MerchantAuthentication,
) -> RouterResult<api::PaymentsRequest> {
) -> RouterResult<P>
where
P: services::Authenticate,
{
match auth_type {
services::MerchantAuthentication::PublishableKey => {
payload
.client_secret
.get_client_secret()
.check_value_present("client_secret")
.change_context(errors::ApiErrorResponse::MissingRequiredField {
field_name: "client_secret".to_owned(),
@ -546,7 +549,7 @@ pub(crate) fn client_secret_auth(
Ok(payload)
}
services::api::MerchantAuthentication::ApiKey => {
if payload.client_secret.is_some() {
if payload.get_client_secret().is_some() {
Err(report!(errors::ApiErrorResponse::InvalidRequestData {
message: "client_secret is not a valid parameter".to_owned(),
}))
@ -1178,3 +1181,56 @@ pub fn generate_mandate(
(_, _) => None,
}
}
// A function to manually authenticate the client secret
pub(crate) fn authenticate_client_secret(
request_client_secret: Option<&String>,
payment_intent_client_secret: Option<&String>,
) -> Result<(), errors::ApiErrorResponse> {
match (request_client_secret, payment_intent_client_secret) {
(Some(req_cs), Some(pi_cs)) => utils::when(
req_cs.ne(pi_cs),
Err(errors::ApiErrorResponse::ClientSecretInvalid),
),
_ => Ok(()),
}
}
// A function to perform database lookup and then verify the client secret
pub(crate) async fn verify_client_secret(
db: &dyn StorageInterface,
storage_scheme: storage_enums::MerchantStorageScheme,
client_secret: Option<String>,
merchant_id: &str,
) -> error_stack::Result<(), errors::ApiErrorResponse> {
match client_secret {
None => Ok(()),
Some(cs) => {
let payment_id = cs.split('_').take(2).collect::<Vec<&str>>().join("_");
let payment_intent = db
.find_payment_intent_by_payment_id_merchant_id(
&payment_id,
merchant_id,
storage_scheme,
)
.await
.change_context(errors::ApiErrorResponse::PaymentNotFound)?;
authenticate_client_secret(Some(&cs), payment_intent.client_secret.as_ref())
.map_err(|err| err.into())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_authenticate_client_secret() {
let req_cs = Some("1".to_string());
let pi_cs = Some("2".to_string());
assert!(authenticate_client_secret(req_cs.as_ref(), pi_cs.as_ref()).is_err())
}
}

View File

@ -61,13 +61,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
})?;
if let Some(ref req_cs) = request.client_secret {
if let Some(ref pi_cs) = payment_intent.client_secret {
if req_cs.ne(pi_cs) {
return Err(report!(errors::ApiErrorResponse::ClientSecretInvalid));
}
}
}
helpers::authenticate_client_secret(
request.client_secret.as_ref(),
payment_intent.client_secret.as_ref(),
)?;
let browser_info = request
.browser_info

View File

@ -1,7 +1,7 @@
use std::marker::PhantomData;
use async_trait::async_trait;
use error_stack::{report, ResultExt};
use error_stack::ResultExt;
use router_derive::PaymentOperation;
use router_env::{instrument, tracing};
@ -73,11 +73,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsSessionRequest>
let amount = payment_intent.amount.into();
if let Some(ref payment_intent_client_secret) = payment_intent.client_secret {
if request.client_secret.ne(payment_intent_client_secret) {
return Err(report!(errors::ApiErrorResponse::ClientSecretInvalid));
}
}
helpers::authenticate_client_secret(
Some(&request.client_secret),
payment_intent.client_secret.as_ref(),
)?;
let shipping_address = helpers::get_address_for_payment_request(
db,

View File

@ -85,13 +85,10 @@ impl<F: Send + Clone> GetTracker<F, PaymentData<F>, api::PaymentsRequest> for Pa
error.to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)
})?;
if let Some(ref req_cs) = request.client_secret {
if let Some(ref pi_cs) = payment_intent.client_secret {
if req_cs.ne(pi_cs) {
return Err(report!(errors::ApiErrorResponse::ClientSecretInvalid));
}
}
}
helpers::authenticate_client_secret(
request.client_secret.as_ref(),
payment_intent.client_secret.as_ref(),
)?;
let shipping_address = helpers::get_address_for_payment_request(
db,

View File

@ -10,6 +10,7 @@ use crate::{
errors::{http_not_implemented, BachResult},
payment_methods::cards,
},
services,
services::api,
types::api::payment_methods::{self, PaymentMethodId},
};
@ -44,14 +45,20 @@ pub async fn list_payment_method_api(
json_payload: web::Query<payment_methods::ListPaymentMethodRequest>,
) -> HttpResponse {
//let merchant_id = merchant_id.into_inner();
let (payload, auth_type) =
match api::get_auth_type_and_check_client_secret(&req, json_payload.into_inner()) {
Ok(values) => values,
Err(err) => return api::log_and_return_error_response(err),
};
api::server_wrap(
&state,
&req,
json_payload.into_inner(),
payload,
|state, merchant_account, req| {
cards::list_payment_methods(&*state.store, merchant_account, req)
},
api::MerchantAuthentication::ApiKey,
auth_type,
)
.await
}
@ -65,6 +72,13 @@ pub async fn list_customer_payment_method_api(
json_payload: web::Query<payment_methods::ListPaymentMethodRequest>,
) -> HttpResponse {
let customer_id = customer_id.into_inner().0;
let auth_type =
match services::authenticate_eph_key(&req, &*state.store, customer_id.clone()).await {
Ok(auth_type) => auth_type,
Err(err) => return api::log_and_return_error_response(err),
};
api::server_wrap(
&state,
&req,
@ -72,7 +86,7 @@ pub async fn list_customer_payment_method_api(
|state, merchant_account, _| {
cards::list_customer_payment_method(state, merchant_account, &customer_id)
},
api::MerchantAuthentication::ApiKey,
auth_type,
)
.await
}

View File

@ -586,10 +586,13 @@ pub async fn authenticate_connector<'a>(
}
}
pub(crate) fn get_auth_type_and_check_client_secret(
pub(crate) fn get_auth_type_and_check_client_secret<P>(
req: &actix_web::HttpRequest,
payload: types::api::PaymentsRequest,
) -> RouterResult<(types::api::PaymentsRequest, MerchantAuthentication)> {
payload: P,
) -> RouterResult<(P, MerchantAuthentication)>
where
P: Authenticate,
{
let auth_type = get_auth_type(req)?;
Ok((
payments::helpers::client_secret_auth(payload, &auth_type)?,
@ -701,6 +704,22 @@ pub trait ConnectorRedirectResponse {
}
}
pub trait Authenticate {
fn get_client_secret(&self) -> Option<&String>;
}
impl Authenticate for api_models::payments::PaymentsRequest {
fn get_client_secret(&self) -> Option<&String> {
self.client_secret.as_ref()
}
}
impl Authenticate for api_models::payment_methods::ListPaymentMethodRequest {
fn get_client_secret(&self) -> Option<&String> {
self.client_secret.as_ref()
}
}
pub fn build_redirection_form(form: &RedirectForm) -> maud::Markup {
use maud::PreEscaped;