mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +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 payment_methods;
|
||||
pub mod payments;
|
||||
#[cfg(feature = "payouts")]
|
||||
pub mod payouts;
|
||||
pub mod refunds;
|
||||
pub mod webhooks;
|
||||
|
||||
@ -1,21 +1,14 @@
|
||||
#[cfg(feature = "payouts")]
|
||||
use cards::CardNumber;
|
||||
#[cfg(feature = "payouts")]
|
||||
use common_utils::{
|
||||
crypto,
|
||||
pii::{self, Email},
|
||||
};
|
||||
#[cfg(feature = "payouts")]
|
||||
use masking::Secret;
|
||||
#[cfg(feature = "payouts")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "payouts")]
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
use crate::{admin, enums as api_enums, payments};
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, ToSchema)]
|
||||
pub enum PayoutRequest {
|
||||
PayoutActionRequest(PayoutActionRequest),
|
||||
@ -23,7 +16,6 @@ pub enum PayoutRequest {
|
||||
PayoutRetrieveRequest(PayoutRetrieveRequest),
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Default, Debug, Deserialize, Serialize, Clone, ToSchema)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PayoutCreateRequest {
|
||||
@ -156,7 +148,6 @@ pub struct PayoutCreateRequest {
|
||||
pub payout_token: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PayoutMethodData {
|
||||
@ -164,14 +155,12 @@ pub enum PayoutMethodData {
|
||||
Bank(Bank),
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
impl Default for PayoutMethodData {
|
||||
fn default() -> Self {
|
||||
Self::Card(Card::default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||
pub struct Card {
|
||||
/// The card number
|
||||
@ -191,7 +180,6 @@ pub struct Card {
|
||||
pub card_holder_name: Secret<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||
#[serde(untagged)]
|
||||
pub enum Bank {
|
||||
@ -200,7 +188,6 @@ pub enum Bank {
|
||||
Sepa(SepaBankTransfer),
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||
pub struct AchBankTransfer {
|
||||
/// Bank name
|
||||
@ -224,7 +211,6 @@ pub struct AchBankTransfer {
|
||||
pub bank_routing_number: Secret<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Default, Eq, PartialEq, Clone, Debug, Deserialize, Serialize, ToSchema)]
|
||||
pub struct BacsBankTransfer {
|
||||
/// Bank name
|
||||
@ -248,7 +234,6 @@ pub struct BacsBankTransfer {
|
||||
pub bank_sort_code: Secret<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[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.
|
||||
pub struct SepaBankTransfer {
|
||||
@ -273,7 +258,6 @@ pub struct SepaBankTransfer {
|
||||
pub bic: Option<Secret<String>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Debug, ToSchema, Clone, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct PayoutCreateResponse {
|
||||
@ -394,13 +378,11 @@ pub struct PayoutCreateResponse {
|
||||
pub error_code: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Default, Debug, Clone, Deserialize, ToSchema)]
|
||||
pub struct PayoutRetrieveBody {
|
||||
pub force_sync: Option<bool>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Default, Debug, Serialize, ToSchema, Clone, Deserialize)]
|
||||
pub struct PayoutRetrieveRequest {
|
||||
/// Unique identifier for the payout. This ensures idempotency for multiple payouts
|
||||
@ -419,7 +401,6 @@ pub struct PayoutRetrieveRequest {
|
||||
pub force_sync: Option<bool>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "payouts")]
|
||||
#[derive(Default, Debug, Serialize, ToSchema, Clone, Deserialize)]
|
||||
pub struct PayoutActionRequest {
|
||||
/// Unique identifier for the payout. This ensures idempotency for multiple payouts
|
||||
|
||||
@ -13,8 +13,6 @@ use api_models::{
|
||||
},
|
||||
payments::BankCodeResponse,
|
||||
};
|
||||
#[cfg(feature = "payouts")]
|
||||
use common_utils::ext_traits::ByteSliceExt;
|
||||
use common_utils::{
|
||||
consts,
|
||||
ext_traits::{AsyncExt, StringExt, ValueExt},
|
||||
@ -257,8 +255,20 @@ pub async fn add_card_hs(
|
||||
customer_id: String,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
) -> errors::CustomResult<(api::PaymentMethodResponse, bool), errors::VaultError> {
|
||||
let store_card_payload =
|
||||
call_to_card_hs(state, &card, None, &customer_id, merchant_account).await?;
|
||||
let payload = payment_methods::StoreLockerReq::LockerCard(payment_methods::StoreCardReq {
|
||||
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(
|
||||
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)]
|
||||
pub async fn get_payment_method_from_hs_locker<'a>(
|
||||
state: &'a routes::AppState,
|
||||
@ -286,7 +318,7 @@ pub async fn get_payment_method_from_hs_locker<'a>(
|
||||
#[cfg(feature = "kms")]
|
||||
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(
|
||||
jwekey,
|
||||
locker,
|
||||
@ -315,44 +347,34 @@ pub async fn get_payment_method_from_hs_locker<'a>(
|
||||
.payload
|
||||
.get_required_value("RetrieveCardRespPayload")
|
||||
.change_context(errors::VaultError::FetchPaymentMethodFailed)?;
|
||||
retrieve_card_resp
|
||||
let enc_card_data = retrieve_card_resp
|
||||
.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 {
|
||||
let get_card_resp =
|
||||
mock_get_payment_method(&*state.store, key_store, payment_method_reference).await?;
|
||||
Ok(get_card_resp.payment_method.payment_method_data)
|
||||
}
|
||||
mock_get_payment_method(&*state.store, key_store, payment_method_reference)
|
||||
.await?
|
||||
.payment_method
|
||||
.payment_method_data
|
||||
};
|
||||
Ok(payment_method_data)
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
pub async fn call_to_card_hs(
|
||||
pub async fn call_to_locker_hs<'a>(
|
||||
state: &routes::AppState,
|
||||
card: &api::CardDetail,
|
||||
enc_value: Option<&str>,
|
||||
payload: &payment_methods::StoreLockerReq<'a>,
|
||||
customer_id: &str,
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
) -> errors::CustomResult<payment_methods::StoreCardRespPayload, errors::VaultError> {
|
||||
let locker = &state.conf.locker;
|
||||
#[cfg(not(feature = "kms"))]
|
||||
let jwekey = &state.conf.jwekey;
|
||||
#[cfg(feature = "kms")]
|
||||
let jwekey = &state.kms_secrets;
|
||||
|
||||
let db = &*state.store;
|
||||
let merchant_id = &merchant_account.merchant_id;
|
||||
|
||||
let stored_card_response = if !locker.mock_locker {
|
||||
let request = payment_methods::mk_add_card_request_hs(
|
||||
jwekey,
|
||||
locker,
|
||||
card,
|
||||
enc_value,
|
||||
customer_id,
|
||||
merchant_id,
|
||||
)
|
||||
.await?;
|
||||
let request = payment_methods::mk_add_locker_request_hs(jwekey, locker, payload).await?;
|
||||
let response = services::call_connector_api(state, request)
|
||||
.await
|
||||
.change_context(errors::VaultError::SaveCardFailed);
|
||||
@ -371,7 +393,7 @@ pub async fn call_to_card_hs(
|
||||
stored_card_resp
|
||||
} else {
|
||||
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
|
||||
@ -495,31 +517,47 @@ pub async fn delete_card_from_hs_locker<'a>(
|
||||
}
|
||||
|
||||
///Mock api for local testing
|
||||
#[instrument(skip_all)]
|
||||
pub async fn mock_add_card_hs(
|
||||
pub async fn mock_call_to_locker_hs<'a>(
|
||||
db: &dyn db::StorageInterface,
|
||||
card_id: &str,
|
||||
card: &api::CardDetail,
|
||||
payload: &payment_methods::StoreLockerReq<'a>,
|
||||
card_cvc: Option<String>,
|
||||
enc_val: Option<&str>,
|
||||
payment_method_id: Option<String>,
|
||||
customer_id: Option<&str>,
|
||||
) -> 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(),
|
||||
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(),
|
||||
card_exp_month: card.card_exp_month.peek().to_string(),
|
||||
merchant_id: "".to_string(),
|
||||
card_number: "4111111111111111".to_string(),
|
||||
card_exp_year: "2099".to_string(),
|
||||
card_exp_month: "12".to_string(),
|
||||
card_cvc,
|
||||
payment_method_id,
|
||||
customer_id: customer_id.map(str::to_string),
|
||||
name_on_card: card.card_holder_name.to_owned().expose_option(),
|
||||
nickname: card.nick_name.to_owned().map(masking::Secret::expose),
|
||||
enc_card_data: enc_val.map(|e| e.to_string()),
|
||||
name_on_card: None,
|
||||
nickname: None,
|
||||
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
|
||||
@ -588,21 +626,7 @@ pub async fn mock_get_payment_method<'a>(
|
||||
.await
|
||||
.change_context(errors::VaultError::FetchPaymentMethodFailed)?;
|
||||
let dec_data = if let Some(e) = locker_mock_up.enc_card_data {
|
||||
// Fetch key
|
||||
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()),
|
||||
)
|
||||
decode_and_decrypt_locker_data(key_store, e).await
|
||||
} else {
|
||||
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")?;
|
||||
let pm_parsed: api::PayoutMethodData = payment_method
|
||||
.peek()
|
||||
.as_bytes()
|
||||
.to_vec()
|
||||
.to_string()
|
||||
.parse_struct("PayoutMethodData")
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
match &pm_parsed {
|
||||
|
||||
@ -15,12 +15,26 @@ use crate::{
|
||||
utils::{self, OptionExt},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum StoreLockerReq<'a> {
|
||||
LockerCard(StoreCardReq<'a>),
|
||||
LockerGeneric(StoreGenericReq<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct StoreCardReq<'a> {
|
||||
pub merchant_id: &'a str,
|
||||
pub merchant_customer_id: String,
|
||||
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)]
|
||||
@ -253,32 +267,13 @@ pub async fn mk_basilisk_req(
|
||||
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(feature = "kms")] jwekey: &settings::ActiveKmsSecrets,
|
||||
locker: &settings::Locker,
|
||||
card: &api::CardDetail,
|
||||
enc_value: Option<&str>,
|
||||
customer_id: &str,
|
||||
merchant_id: &str,
|
||||
payload: &StoreLockerReq<'a>,
|
||||
) -> CustomResult<services::Request, errors::VaultError> {
|
||||
let merchant_customer_id = customer_id.to_owned();
|
||||
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)
|
||||
let payload = utils::Encode::<StoreCardReq<'_>>::encode_to_vec(&payload)
|
||||
.change_context(errors::VaultError::RequestEncodingFailed)?;
|
||||
|
||||
#[cfg(feature = "kms")]
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use ::cards::CardNumber;
|
||||
use common_utils::{errors::CustomResult, ext_traits::ValueExt};
|
||||
use diesel_models::encryption::Encryption;
|
||||
use error_stack::{IntoReport, ResultExt};
|
||||
@ -9,7 +6,11 @@ use masking::{ExposeInterface, PeekInterface, Secret};
|
||||
use crate::{
|
||||
core::{
|
||||
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},
|
||||
utils as core_utils,
|
||||
},
|
||||
@ -125,21 +126,37 @@ pub async fn save_payout_data_to_locker(
|
||||
merchant_account: &domain::MerchantAccount,
|
||||
key_store: &domain::MerchantKeyStore,
|
||||
) -> RouterResult<()> {
|
||||
let mut enc_card_data = None;
|
||||
let (card_details, payment_method_type) = match payout_method_data {
|
||||
api_models::payouts::PayoutMethodData::Card(card) => (
|
||||
api::CardDetail {
|
||||
let (locker_req, card_details, payment_method_type) = match payout_method_data {
|
||||
api_models::payouts::PayoutMethodData::Card(card) => {
|
||||
let card_detail = api::CardDetail {
|
||||
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_year: card.expiry_year.to_owned(),
|
||||
card_holder_name: Some(card.card_holder_name.to_owned()),
|
||||
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) => {
|
||||
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())
|
||||
.into_report()
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||
@ -160,32 +177,22 @@ pub async fn save_payout_data_to_locker(
|
||||
.map_or(Err(errors::ApiErrorResponse::InternalServerError), |e| {
|
||||
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 {
|
||||
card_number: CardNumber::from_str("4111111111111111")
|
||||
.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,
|
||||
},
|
||||
payload,
|
||||
None,
|
||||
api_enums::PaymentMethodType::foreign_from(bank.to_owned()),
|
||||
)
|
||||
}
|
||||
};
|
||||
// Store payout method in locker
|
||||
let stored_resp = cards::call_to_card_hs(
|
||||
state,
|
||||
&card_details,
|
||||
enc_card_data.as_deref(),
|
||||
&payout_attempt.customer_id,
|
||||
merchant_account,
|
||||
)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
let stored_resp = cards::call_to_locker_hs(state, &locker_req, &payout_attempt.customer_id)
|
||||
.await
|
||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||
|
||||
// Store card_reference in payouts table
|
||||
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_issuer: None,
|
||||
payment_method_issuer_code: None,
|
||||
card: Some(card_details),
|
||||
card: card_details,
|
||||
metadata: None,
|
||||
customer_id: Some(payout_attempt.customer_id.to_owned()),
|
||||
card_network: None,
|
||||
|
||||
Reference in New Issue
Block a user