fix: storage of generic payment methods in permanent locker (#1799)

Co-authored-by: Kashif <mohammed.kashif@juspay.in>
This commit is contained in:
Kashif
2023-08-22 12:19:43 +05:30
committed by GitHub
parent bb9c34e8b3
commit 19ee324d37
5 changed files with 140 additions and 133 deletions

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

@ -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")]

View File

@ -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,