mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
feat(list_pm): allow client secret authentication for list payment method api (#126)
This commit is contained in:
@ -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>,
|
||||
|
||||
@ -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(),
|
||||
};
|
||||
|
||||
@ -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))?;
|
||||
|
||||
|
||||
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user