mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
fix: storage of generic payment methods in permanent locker (#1799)
Co-authored-by: Kashif <mohammed.kashif@juspay.in>
This commit is contained in:
@ -13,6 +13,7 @@ pub mod files;
|
|||||||
pub mod mandates;
|
pub mod mandates;
|
||||||
pub mod payment_methods;
|
pub mod payment_methods;
|
||||||
pub mod payments;
|
pub mod payments;
|
||||||
|
#[cfg(feature = "payouts")]
|
||||||
pub mod payouts;
|
pub mod payouts;
|
||||||
pub mod refunds;
|
pub mod refunds;
|
||||||
pub mod webhooks;
|
pub mod webhooks;
|
||||||
|
|||||||
@ -1,21 +1,14 @@
|
|||||||
#[cfg(feature = "payouts")]
|
|
||||||
use cards::CardNumber;
|
use cards::CardNumber;
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
use common_utils::{
|
use common_utils::{
|
||||||
crypto,
|
crypto,
|
||||||
pii::{self, Email},
|
pii::{self, Email},
|
||||||
};
|
};
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
use masking::Secret;
|
use masking::Secret;
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
use utoipa::ToSchema;
|
use utoipa::ToSchema;
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
use crate::{admin, enums as api_enums, payments};
|
use crate::{admin, enums as api_enums, payments};
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
|
#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
|
||||||
pub enum PayoutRequest {
|
pub enum PayoutRequest {
|
||||||
PayoutActionRequest(PayoutActionRequest),
|
PayoutActionRequest(PayoutActionRequest),
|
||||||
@ -23,7 +16,6 @@ pub enum PayoutRequest {
|
|||||||
PayoutRetrieveRequest(PayoutRetrieveRequest),
|
PayoutRetrieveRequest(PayoutRetrieveRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize, Clone, ToSchema)]
|
#[derive(Default, Debug, Deserialize, Serialize, Clone, ToSchema)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct PayoutCreateRequest {
|
pub struct PayoutCreateRequest {
|
||||||
@ -156,7 +148,6 @@ pub struct PayoutCreateRequest {
|
|||||||
pub payout_token: Option<String>,
|
pub payout_token: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum PayoutMethodData {
|
pub enum PayoutMethodData {
|
||||||
@ -164,14 +155,12 @@ pub enum PayoutMethodData {
|
|||||||
Bank(Bank),
|
Bank(Bank),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
impl Default for PayoutMethodData {
|
impl Default for PayoutMethodData {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Card(Card::default())
|
Self::Card(Card::default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||||
pub struct Card {
|
pub struct Card {
|
||||||
/// The card number
|
/// The card number
|
||||||
@ -191,7 +180,6 @@ pub struct Card {
|
|||||||
pub card_holder_name: Secret<String>,
|
pub card_holder_name: Secret<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub enum Bank {
|
pub enum Bank {
|
||||||
@ -200,7 +188,6 @@ pub enum Bank {
|
|||||||
Sepa(SepaBankTransfer),
|
Sepa(SepaBankTransfer),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||||
pub struct AchBankTransfer {
|
pub struct AchBankTransfer {
|
||||||
/// Bank name
|
/// Bank name
|
||||||
@ -224,7 +211,6 @@ pub struct AchBankTransfer {
|
|||||||
pub bank_routing_number: Secret<String>,
|
pub bank_routing_number: Secret<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||||
pub struct BacsBankTransfer {
|
pub struct BacsBankTransfer {
|
||||||
/// Bank name
|
/// Bank name
|
||||||
@ -248,7 +234,6 @@ pub struct BacsBankTransfer {
|
|||||||
pub bank_sort_code: Secret<String>,
|
pub bank_sort_code: Secret<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||||
// The SEPA (Single Euro Payments Area) is a pan-European network that allows you to send and receive payments in euros between two cross-border bank accounts in the eurozone.
|
// The SEPA (Single Euro Payments Area) is a pan-European network that allows you to send and receive payments in euros between two cross-border bank accounts in the eurozone.
|
||||||
pub struct SepaBankTransfer {
|
pub struct SepaBankTransfer {
|
||||||
@ -273,7 +258,6 @@ pub struct SepaBankTransfer {
|
|||||||
pub bic: Option<Secret<String>>,
|
pub bic: Option<Secret<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Debug, ToSchema, Clone, Serialize)]
|
#[derive(Debug, ToSchema, Clone, Serialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct PayoutCreateResponse {
|
pub struct PayoutCreateResponse {
|
||||||
@ -394,13 +378,11 @@ pub struct PayoutCreateResponse {
|
|||||||
pub error_code: Option<String>,
|
pub error_code: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Debug, Clone, Deserialize, ToSchema)]
|
#[derive(Default, Debug, Clone, Deserialize, ToSchema)]
|
||||||
pub struct PayoutRetrieveBody {
|
pub struct PayoutRetrieveBody {
|
||||||
pub force_sync: Option<bool>,
|
pub force_sync: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Debug, Serialize, ToSchema, Clone, Deserialize)]
|
#[derive(Default, Debug, Serialize, ToSchema, Clone, Deserialize)]
|
||||||
pub struct PayoutRetrieveRequest {
|
pub struct PayoutRetrieveRequest {
|
||||||
/// Unique identifier for the payout. This ensures idempotency for multiple payouts
|
/// Unique identifier for the payout. This ensures idempotency for multiple payouts
|
||||||
@ -419,7 +401,6 @@ pub struct PayoutRetrieveRequest {
|
|||||||
pub force_sync: Option<bool>,
|
pub force_sync: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
#[derive(Default, Debug, Serialize, ToSchema, Clone, Deserialize)]
|
#[derive(Default, Debug, Serialize, ToSchema, Clone, Deserialize)]
|
||||||
pub struct PayoutActionRequest {
|
pub struct PayoutActionRequest {
|
||||||
/// Unique identifier for the payout. This ensures idempotency for multiple payouts
|
/// Unique identifier for the payout. This ensures idempotency for multiple payouts
|
||||||
|
|||||||
@ -13,8 +13,6 @@ use api_models::{
|
|||||||
},
|
},
|
||||||
payments::BankCodeResponse,
|
payments::BankCodeResponse,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "payouts")]
|
|
||||||
use common_utils::ext_traits::ByteSliceExt;
|
|
||||||
use common_utils::{
|
use common_utils::{
|
||||||
consts,
|
consts,
|
||||||
ext_traits::{AsyncExt, StringExt, ValueExt},
|
ext_traits::{AsyncExt, StringExt, ValueExt},
|
||||||
@ -257,8 +255,20 @@ pub async fn add_card_hs(
|
|||||||
customer_id: String,
|
customer_id: String,
|
||||||
merchant_account: &domain::MerchantAccount,
|
merchant_account: &domain::MerchantAccount,
|
||||||
) -> errors::CustomResult<(api::PaymentMethodResponse, bool), errors::VaultError> {
|
) -> errors::CustomResult<(api::PaymentMethodResponse, bool), errors::VaultError> {
|
||||||
let store_card_payload =
|
let payload = payment_methods::StoreLockerReq::LockerCard(payment_methods::StoreCardReq {
|
||||||
call_to_card_hs(state, &card, None, &customer_id, merchant_account).await?;
|
merchant_id: &merchant_account.merchant_id,
|
||||||
|
merchant_customer_id: customer_id.to_owned(),
|
||||||
|
card: payment_methods::Card {
|
||||||
|
card_number: card.card_number.to_owned(),
|
||||||
|
name_on_card: card.card_holder_name.to_owned(),
|
||||||
|
card_exp_month: card.card_exp_month.to_owned(),
|
||||||
|
card_exp_year: card.card_exp_year.to_owned(),
|
||||||
|
card_brand: None,
|
||||||
|
card_isin: None,
|
||||||
|
nick_name: card.nick_name.as_ref().map(masking::Secret::peek).cloned(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
let store_card_payload = call_to_locker_hs(state, &payload, &customer_id).await?;
|
||||||
|
|
||||||
let payment_method_resp = payment_methods::mk_add_card_response_hs(
|
let payment_method_resp = payment_methods::mk_add_card_response_hs(
|
||||||
card,
|
card,
|
||||||
@ -272,6 +282,28 @@ pub async fn add_card_hs(
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub async fn decode_and_decrypt_locker_data(
|
||||||
|
key_store: &domain::MerchantKeyStore,
|
||||||
|
enc_card_data: String,
|
||||||
|
) -> errors::CustomResult<Secret<String>, errors::VaultError> {
|
||||||
|
// Fetch key
|
||||||
|
let key = key_store.key.get_inner().peek();
|
||||||
|
// Decode
|
||||||
|
let decoded_bytes = hex::decode(&enc_card_data)
|
||||||
|
.into_report()
|
||||||
|
.change_context(errors::VaultError::ResponseDeserializationFailed)
|
||||||
|
.attach_printable("Failed to decode hex string into bytes")?;
|
||||||
|
// Decrypt
|
||||||
|
decrypt(Some(Encryption::new(decoded_bytes.into())), key)
|
||||||
|
.await
|
||||||
|
.change_context(errors::VaultError::FetchPaymentMethodFailed)?
|
||||||
|
.map_or(
|
||||||
|
Err(report!(errors::VaultError::FetchPaymentMethodFailed)),
|
||||||
|
|d| Ok(d.into_inner()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn get_payment_method_from_hs_locker<'a>(
|
pub async fn get_payment_method_from_hs_locker<'a>(
|
||||||
state: &'a routes::AppState,
|
state: &'a routes::AppState,
|
||||||
@ -286,7 +318,7 @@ pub async fn get_payment_method_from_hs_locker<'a>(
|
|||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let jwekey = &state.kms_secrets;
|
let jwekey = &state.kms_secrets;
|
||||||
|
|
||||||
if !locker.mock_locker {
|
let payment_method_data = if !locker.mock_locker {
|
||||||
let request = payment_methods::mk_get_card_request_hs(
|
let request = payment_methods::mk_get_card_request_hs(
|
||||||
jwekey,
|
jwekey,
|
||||||
locker,
|
locker,
|
||||||
@ -315,44 +347,34 @@ pub async fn get_payment_method_from_hs_locker<'a>(
|
|||||||
.payload
|
.payload
|
||||||
.get_required_value("RetrieveCardRespPayload")
|
.get_required_value("RetrieveCardRespPayload")
|
||||||
.change_context(errors::VaultError::FetchPaymentMethodFailed)?;
|
.change_context(errors::VaultError::FetchPaymentMethodFailed)?;
|
||||||
retrieve_card_resp
|
let enc_card_data = retrieve_card_resp
|
||||||
.enc_card_data
|
.enc_card_data
|
||||||
.get_required_value("enc_card_data")
|
.get_required_value("enc_card_data")
|
||||||
.change_context(errors::VaultError::FetchPaymentMethodFailed)
|
.change_context(errors::VaultError::FetchPaymentMethodFailed)?;
|
||||||
|
decode_and_decrypt_locker_data(key_store, enc_card_data.peek().to_string()).await?
|
||||||
} else {
|
} else {
|
||||||
let get_card_resp =
|
mock_get_payment_method(&*state.store, key_store, payment_method_reference)
|
||||||
mock_get_payment_method(&*state.store, key_store, payment_method_reference).await?;
|
.await?
|
||||||
Ok(get_card_resp.payment_method.payment_method_data)
|
.payment_method
|
||||||
}
|
.payment_method_data
|
||||||
|
};
|
||||||
|
Ok(payment_method_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
pub async fn call_to_card_hs(
|
pub async fn call_to_locker_hs<'a>(
|
||||||
state: &routes::AppState,
|
state: &routes::AppState,
|
||||||
card: &api::CardDetail,
|
payload: &payment_methods::StoreLockerReq<'a>,
|
||||||
enc_value: Option<&str>,
|
|
||||||
customer_id: &str,
|
customer_id: &str,
|
||||||
merchant_account: &domain::MerchantAccount,
|
|
||||||
) -> errors::CustomResult<payment_methods::StoreCardRespPayload, errors::VaultError> {
|
) -> errors::CustomResult<payment_methods::StoreCardRespPayload, errors::VaultError> {
|
||||||
let locker = &state.conf.locker;
|
let locker = &state.conf.locker;
|
||||||
#[cfg(not(feature = "kms"))]
|
#[cfg(not(feature = "kms"))]
|
||||||
let jwekey = &state.conf.jwekey;
|
let jwekey = &state.conf.jwekey;
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let jwekey = &state.kms_secrets;
|
let jwekey = &state.kms_secrets;
|
||||||
|
|
||||||
let db = &*state.store;
|
let db = &*state.store;
|
||||||
let merchant_id = &merchant_account.merchant_id;
|
|
||||||
|
|
||||||
let stored_card_response = if !locker.mock_locker {
|
let stored_card_response = if !locker.mock_locker {
|
||||||
let request = payment_methods::mk_add_card_request_hs(
|
let request = payment_methods::mk_add_locker_request_hs(jwekey, locker, payload).await?;
|
||||||
jwekey,
|
|
||||||
locker,
|
|
||||||
card,
|
|
||||||
enc_value,
|
|
||||||
customer_id,
|
|
||||||
merchant_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let response = services::call_connector_api(state, request)
|
let response = services::call_connector_api(state, request)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::VaultError::SaveCardFailed);
|
.change_context(errors::VaultError::SaveCardFailed);
|
||||||
@ -371,7 +393,7 @@ pub async fn call_to_card_hs(
|
|||||||
stored_card_resp
|
stored_card_resp
|
||||||
} else {
|
} else {
|
||||||
let card_id = generate_id(consts::ID_LENGTH, "card");
|
let card_id = generate_id(consts::ID_LENGTH, "card");
|
||||||
mock_add_card_hs(db, &card_id, card, None, enc_value, None, Some(customer_id)).await?
|
mock_call_to_locker_hs(db, &card_id, payload, None, None, Some(customer_id)).await?
|
||||||
};
|
};
|
||||||
|
|
||||||
let stored_card = stored_card_response
|
let stored_card = stored_card_response
|
||||||
@ -495,31 +517,47 @@ pub async fn delete_card_from_hs_locker<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
///Mock api for local testing
|
///Mock api for local testing
|
||||||
#[instrument(skip_all)]
|
pub async fn mock_call_to_locker_hs<'a>(
|
||||||
pub async fn mock_add_card_hs(
|
|
||||||
db: &dyn db::StorageInterface,
|
db: &dyn db::StorageInterface,
|
||||||
card_id: &str,
|
card_id: &str,
|
||||||
card: &api::CardDetail,
|
payload: &payment_methods::StoreLockerReq<'a>,
|
||||||
card_cvc: Option<String>,
|
card_cvc: Option<String>,
|
||||||
enc_val: Option<&str>,
|
|
||||||
payment_method_id: Option<String>,
|
payment_method_id: Option<String>,
|
||||||
customer_id: Option<&str>,
|
customer_id: Option<&str>,
|
||||||
) -> errors::CustomResult<payment_methods::StoreCardResp, errors::VaultError> {
|
) -> errors::CustomResult<payment_methods::StoreCardResp, errors::VaultError> {
|
||||||
let locker_mock_up = storage::LockerMockUpNew {
|
let mut locker_mock_up = storage::LockerMockUpNew {
|
||||||
card_id: card_id.to_string(),
|
card_id: card_id.to_string(),
|
||||||
external_id: uuid::Uuid::new_v4().to_string(),
|
external_id: uuid::Uuid::new_v4().to_string(),
|
||||||
card_fingerprint: uuid::Uuid::new_v4().to_string(),
|
card_fingerprint: uuid::Uuid::new_v4().to_string(),
|
||||||
card_global_fingerprint: uuid::Uuid::new_v4().to_string(),
|
card_global_fingerprint: uuid::Uuid::new_v4().to_string(),
|
||||||
merchant_id: "mm01".to_string(),
|
merchant_id: "".to_string(),
|
||||||
card_number: card.card_number.peek().to_string(),
|
card_number: "4111111111111111".to_string(),
|
||||||
card_exp_year: card.card_exp_year.peek().to_string(),
|
card_exp_year: "2099".to_string(),
|
||||||
card_exp_month: card.card_exp_month.peek().to_string(),
|
card_exp_month: "12".to_string(),
|
||||||
card_cvc,
|
card_cvc,
|
||||||
payment_method_id,
|
payment_method_id,
|
||||||
customer_id: customer_id.map(str::to_string),
|
customer_id: customer_id.map(str::to_string),
|
||||||
name_on_card: card.card_holder_name.to_owned().expose_option(),
|
name_on_card: None,
|
||||||
nickname: card.nick_name.to_owned().map(masking::Secret::expose),
|
nickname: None,
|
||||||
enc_card_data: enc_val.map(|e| e.to_string()),
|
enc_card_data: None,
|
||||||
|
};
|
||||||
|
locker_mock_up = match payload {
|
||||||
|
payment_methods::StoreLockerReq::LockerCard(store_card_req) => storage::LockerMockUpNew {
|
||||||
|
merchant_id: store_card_req.merchant_id.to_string(),
|
||||||
|
card_number: store_card_req.card.card_number.peek().to_string(),
|
||||||
|
card_exp_year: store_card_req.card.card_exp_year.peek().to_string(),
|
||||||
|
card_exp_month: store_card_req.card.card_exp_month.peek().to_string(),
|
||||||
|
name_on_card: store_card_req.card.name_on_card.to_owned().expose_option(),
|
||||||
|
nickname: store_card_req.card.nick_name.to_owned(),
|
||||||
|
..locker_mock_up
|
||||||
|
},
|
||||||
|
payment_methods::StoreLockerReq::LockerGeneric(store_generic_req) => {
|
||||||
|
storage::LockerMockUpNew {
|
||||||
|
merchant_id: store_generic_req.merchant_id.to_string(),
|
||||||
|
enc_card_data: Some(store_generic_req.enc_data.to_owned()),
|
||||||
|
..locker_mock_up
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = db
|
let response = db
|
||||||
@ -588,21 +626,7 @@ pub async fn mock_get_payment_method<'a>(
|
|||||||
.await
|
.await
|
||||||
.change_context(errors::VaultError::FetchPaymentMethodFailed)?;
|
.change_context(errors::VaultError::FetchPaymentMethodFailed)?;
|
||||||
let dec_data = if let Some(e) = locker_mock_up.enc_card_data {
|
let dec_data = if let Some(e) = locker_mock_up.enc_card_data {
|
||||||
// Fetch key
|
decode_and_decrypt_locker_data(key_store, e).await
|
||||||
let key = key_store.key.get_inner().peek();
|
|
||||||
// Decode
|
|
||||||
let decoded_bytes = hex::decode(e)
|
|
||||||
.into_report()
|
|
||||||
.change_context(errors::VaultError::ResponseDeserializationFailed)
|
|
||||||
.attach_printable("Failed to decode hex string into bytes")?;
|
|
||||||
// Decrypt
|
|
||||||
async { decrypt(Some(Encryption::new(decoded_bytes.into())), key).await }
|
|
||||||
.await
|
|
||||||
.change_context(errors::VaultError::FetchPaymentMethodFailed)?
|
|
||||||
.map_or(
|
|
||||||
Err(report!(errors::VaultError::FetchPaymentMethodFailed)),
|
|
||||||
|d| Ok(d.into_inner()),
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
Err(report!(errors::VaultError::FetchPaymentMethodFailed))
|
Err(report!(errors::VaultError::FetchPaymentMethodFailed))
|
||||||
}?;
|
}?;
|
||||||
@ -1894,8 +1918,7 @@ pub async fn get_lookup_key_for_payout_method(
|
|||||||
.attach_printable("Error getting payment method from locker")?;
|
.attach_printable("Error getting payment method from locker")?;
|
||||||
let pm_parsed: api::PayoutMethodData = payment_method
|
let pm_parsed: api::PayoutMethodData = payment_method
|
||||||
.peek()
|
.peek()
|
||||||
.as_bytes()
|
.to_string()
|
||||||
.to_vec()
|
|
||||||
.parse_struct("PayoutMethodData")
|
.parse_struct("PayoutMethodData")
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||||
match &pm_parsed {
|
match &pm_parsed {
|
||||||
|
|||||||
@ -15,12 +15,26 @@ use crate::{
|
|||||||
utils::{self, OptionExt},
|
utils::{self, OptionExt},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum StoreLockerReq<'a> {
|
||||||
|
LockerCard(StoreCardReq<'a>),
|
||||||
|
LockerGeneric(StoreGenericReq<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct StoreCardReq<'a> {
|
pub struct StoreCardReq<'a> {
|
||||||
pub merchant_id: &'a str,
|
pub merchant_id: &'a str,
|
||||||
pub merchant_customer_id: String,
|
pub merchant_customer_id: String,
|
||||||
pub card: Card,
|
pub card: Card,
|
||||||
pub enc_card_data: Option<String>,
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
|
pub struct StoreGenericReq<'a> {
|
||||||
|
pub merchant_id: &'a str,
|
||||||
|
pub merchant_customer_id: String,
|
||||||
|
#[serde(rename = "enc_card_data")]
|
||||||
|
pub enc_data: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
@ -253,32 +267,13 @@ pub async fn mk_basilisk_req(
|
|||||||
Ok(jwe_body)
|
Ok(jwe_body)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn mk_add_card_request_hs(
|
pub async fn mk_add_locker_request_hs<'a>(
|
||||||
#[cfg(not(feature = "kms"))] jwekey: &settings::Jwekey,
|
#[cfg(not(feature = "kms"))] jwekey: &settings::Jwekey,
|
||||||
#[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets,
|
#[cfg(feature = "kms")] jwekey: &settings::ActiveKmsSecrets,
|
||||||
locker: &settings::Locker,
|
locker: &settings::Locker,
|
||||||
card: &api::CardDetail,
|
payload: &StoreLockerReq<'a>,
|
||||||
enc_value: Option<&str>,
|
|
||||||
customer_id: &str,
|
|
||||||
merchant_id: &str,
|
|
||||||
) -> CustomResult<services::Request, errors::VaultError> {
|
) -> CustomResult<services::Request, errors::VaultError> {
|
||||||
let merchant_customer_id = customer_id.to_owned();
|
let payload = utils::Encode::<StoreCardReq<'_>>::encode_to_vec(&payload)
|
||||||
let card = Card {
|
|
||||||
card_number: card.card_number.to_owned(),
|
|
||||||
name_on_card: card.card_holder_name.to_owned(),
|
|
||||||
card_exp_month: card.card_exp_month.to_owned(),
|
|
||||||
card_exp_year: card.card_exp_year.to_owned(),
|
|
||||||
card_brand: None,
|
|
||||||
card_isin: None,
|
|
||||||
nick_name: card.nick_name.to_owned().map(masking::Secret::expose),
|
|
||||||
};
|
|
||||||
let store_card_req = StoreCardReq {
|
|
||||||
merchant_id,
|
|
||||||
merchant_customer_id,
|
|
||||||
card,
|
|
||||||
enc_card_data: enc_value.map(|e| e.to_string()),
|
|
||||||
};
|
|
||||||
let payload = utils::Encode::<StoreCardReq<'_>>::encode_to_vec(&store_card_req)
|
|
||||||
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
||||||
|
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
|
|||||||
@ -1,6 +1,3 @@
|
|||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use ::cards::CardNumber;
|
|
||||||
use common_utils::{errors::CustomResult, ext_traits::ValueExt};
|
use common_utils::{errors::CustomResult, ext_traits::ValueExt};
|
||||||
use diesel_models::encryption::Encryption;
|
use diesel_models::encryption::Encryption;
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
@ -9,7 +6,11 @@ use masking::{ExposeInterface, PeekInterface, Secret};
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
errors::{self, RouterResult},
|
errors::{self, RouterResult},
|
||||||
payment_methods::{cards, vault},
|
payment_methods::{
|
||||||
|
cards, transformers,
|
||||||
|
transformers::{StoreCardReq, StoreGenericReq, StoreLockerReq},
|
||||||
|
vault,
|
||||||
|
},
|
||||||
payments::{customers::get_connector_customer_details_if_present, CustomerDetails},
|
payments::{customers::get_connector_customer_details_if_present, CustomerDetails},
|
||||||
utils as core_utils,
|
utils as core_utils,
|
||||||
},
|
},
|
||||||
@ -125,21 +126,37 @@ pub async fn save_payout_data_to_locker(
|
|||||||
merchant_account: &domain::MerchantAccount,
|
merchant_account: &domain::MerchantAccount,
|
||||||
key_store: &domain::MerchantKeyStore,
|
key_store: &domain::MerchantKeyStore,
|
||||||
) -> RouterResult<()> {
|
) -> RouterResult<()> {
|
||||||
let mut enc_card_data = None;
|
let (locker_req, card_details, payment_method_type) = match payout_method_data {
|
||||||
let (card_details, payment_method_type) = match payout_method_data {
|
api_models::payouts::PayoutMethodData::Card(card) => {
|
||||||
api_models::payouts::PayoutMethodData::Card(card) => (
|
let card_detail = api::CardDetail {
|
||||||
api::CardDetail {
|
|
||||||
card_number: card.card_number.to_owned(),
|
card_number: card.card_number.to_owned(),
|
||||||
|
card_holder_name: Some(card.card_holder_name.to_owned()),
|
||||||
card_exp_month: card.expiry_month.to_owned(),
|
card_exp_month: card.expiry_month.to_owned(),
|
||||||
card_exp_year: card.expiry_year.to_owned(),
|
card_exp_year: card.expiry_year.to_owned(),
|
||||||
card_holder_name: Some(card.card_holder_name.to_owned()),
|
|
||||||
nick_name: None,
|
nick_name: None,
|
||||||
},
|
};
|
||||||
api_enums::PaymentMethodType::Debit,
|
let payload = StoreLockerReq::LockerCard(StoreCardReq {
|
||||||
),
|
merchant_id: &merchant_account.merchant_id,
|
||||||
|
merchant_customer_id: payout_attempt.customer_id.to_owned(),
|
||||||
|
card: transformers::Card {
|
||||||
|
card_number: card.card_number.to_owned(),
|
||||||
|
name_on_card: Some(card.card_holder_name.to_owned()),
|
||||||
|
card_exp_month: card.expiry_month.to_owned(),
|
||||||
|
card_exp_year: card.expiry_year.to_owned(),
|
||||||
|
card_brand: None,
|
||||||
|
card_isin: None,
|
||||||
|
nick_name: None,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
(
|
||||||
|
payload,
|
||||||
|
Some(card_detail),
|
||||||
|
api_enums::PaymentMethodType::Debit,
|
||||||
|
)
|
||||||
|
}
|
||||||
api_models::payouts::PayoutMethodData::Bank(bank) => {
|
api_models::payouts::PayoutMethodData::Bank(bank) => {
|
||||||
let key = key_store.key.get_inner().peek();
|
let key = key_store.key.get_inner().peek();
|
||||||
let enc_str = async {
|
let enc_data = async {
|
||||||
serde_json::to_value(payout_method_data.to_owned())
|
serde_json::to_value(payout_method_data.to_owned())
|
||||||
.into_report()
|
.into_report()
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
@ -160,32 +177,22 @@ pub async fn save_payout_data_to_locker(
|
|||||||
.map_or(Err(errors::ApiErrorResponse::InternalServerError), |e| {
|
.map_or(Err(errors::ApiErrorResponse::InternalServerError), |e| {
|
||||||
Ok(hex::encode(e.peek()))
|
Ok(hex::encode(e.peek()))
|
||||||
})?;
|
})?;
|
||||||
enc_card_data = Some(enc_str);
|
let payload = StoreLockerReq::LockerGeneric(StoreGenericReq {
|
||||||
|
merchant_id: &merchant_account.merchant_id,
|
||||||
|
merchant_customer_id: payout_attempt.customer_id.to_owned(),
|
||||||
|
enc_data,
|
||||||
|
});
|
||||||
(
|
(
|
||||||
api::CardDetail {
|
payload,
|
||||||
card_number: CardNumber::from_str("4111111111111111")
|
None,
|
||||||
.into_report()
|
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
|
||||||
.attach_printable("Failed to form a sample card number")?,
|
|
||||||
card_exp_month: "12".to_string().into(),
|
|
||||||
card_exp_year: "99".to_string().into(),
|
|
||||||
card_holder_name: None,
|
|
||||||
nick_name: None,
|
|
||||||
},
|
|
||||||
api_enums::PaymentMethodType::foreign_from(bank.to_owned()),
|
api_enums::PaymentMethodType::foreign_from(bank.to_owned()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Store payout method in locker
|
// Store payout method in locker
|
||||||
let stored_resp = cards::call_to_card_hs(
|
let stored_resp = cards::call_to_locker_hs(state, &locker_req, &payout_attempt.customer_id)
|
||||||
state,
|
.await
|
||||||
&card_details,
|
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||||
enc_card_data.as_deref(),
|
|
||||||
&payout_attempt.customer_id,
|
|
||||||
merchant_account,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
|
||||||
|
|
||||||
// Store card_reference in payouts table
|
// Store card_reference in payouts table
|
||||||
let db = &*state.store;
|
let db = &*state.store;
|
||||||
@ -208,7 +215,7 @@ pub async fn save_payout_data_to_locker(
|
|||||||
payment_method_type: Some(payment_method_type),
|
payment_method_type: Some(payment_method_type),
|
||||||
payment_method_issuer: None,
|
payment_method_issuer: None,
|
||||||
payment_method_issuer_code: None,
|
payment_method_issuer_code: None,
|
||||||
card: Some(card_details),
|
card: card_details,
|
||||||
metadata: None,
|
metadata: None,
|
||||||
customer_id: Some(payout_attempt.customer_id.to_owned()),
|
customer_id: Some(payout_attempt.customer_id.to_owned()),
|
||||||
card_network: None,
|
card_network: None,
|
||||||
|
|||||||
Reference in New Issue
Block a user