mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 09:07:09 +08:00
fix(locker): handle card duplication in payouts flow (#4013)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -27,6 +27,15 @@ pub enum StoreLockerReq<'a> {
|
|||||||
LockerGeneric(StoreGenericReq<'a>),
|
LockerGeneric(StoreGenericReq<'a>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StoreLockerReq<'_> {
|
||||||
|
pub fn update_requestor_card_reference(&mut self, card_reference: Option<String>) {
|
||||||
|
match self {
|
||||||
|
Self::LockerCard(c) => c.requestor_card_reference = card_reference,
|
||||||
|
Self::LockerGeneric(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[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,
|
||||||
|
|||||||
@ -1452,7 +1452,10 @@ pub async fn fulfill_payout(
|
|||||||
.status
|
.status
|
||||||
.unwrap_or(payout_attempt.status.to_owned());
|
.unwrap_or(payout_attempt.status.to_owned());
|
||||||
payout_data.payouts.status = status;
|
payout_data.payouts.status = status;
|
||||||
if payout_data.payouts.recurring && payout_data.payouts.payout_method_id.is_none() {
|
if payout_data.payouts.recurring
|
||||||
|
&& payout_data.payouts.payout_method_id.is_none()
|
||||||
|
&& !helpers::is_payout_err_state(status)
|
||||||
|
{
|
||||||
helpers::save_payout_data_to_locker(
|
helpers::save_payout_data_to_locker(
|
||||||
state,
|
state,
|
||||||
payout_data,
|
payout_data,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use api_models::enums::PayoutConnectors;
|
use api_models::{enums, payouts};
|
||||||
use common_utils::{
|
use common_utils::{
|
||||||
errors::CustomResult,
|
errors::CustomResult,
|
||||||
ext_traits::{AsyncExt, StringExt},
|
ext_traits::{AsyncExt, StringExt},
|
||||||
@ -11,10 +11,12 @@ use router_env::logger;
|
|||||||
use super::PayoutData;
|
use super::PayoutData;
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
errors::{self, RouterResult},
|
errors::{self, RouterResult, StorageErrorExt},
|
||||||
payment_methods::{
|
payment_methods::{
|
||||||
cards,
|
cards,
|
||||||
transformers::{self, StoreCardReq, StoreGenericReq, StoreLockerReq},
|
transformers::{
|
||||||
|
self, DataDuplicationCheck, StoreCardReq, StoreGenericReq, StoreLockerReq,
|
||||||
|
},
|
||||||
vault,
|
vault,
|
||||||
},
|
},
|
||||||
payments::{
|
payments::{
|
||||||
@ -191,9 +193,9 @@ pub async fn save_payout_data_to_locker(
|
|||||||
key_store: &domain::MerchantKeyStore,
|
key_store: &domain::MerchantKeyStore,
|
||||||
) -> RouterResult<()> {
|
) -> RouterResult<()> {
|
||||||
let payout_attempt = &payout_data.payout_attempt;
|
let payout_attempt = &payout_data.payout_attempt;
|
||||||
let (locker_req, card_details, bank_details, wallet_details, payment_method_type) =
|
let (mut locker_req, card_details, bank_details, wallet_details, payment_method_type) =
|
||||||
match payout_method_data {
|
match payout_method_data {
|
||||||
api_models::payouts::PayoutMethodData::Card(card) => {
|
payouts::PayoutMethodData::Card(card) => {
|
||||||
let card_detail = api::CardDetail {
|
let card_detail = api::CardDetail {
|
||||||
card_number: card.card_number.to_owned(),
|
card_number: card.card_number.to_owned(),
|
||||||
card_holder_name: card.card_holder_name.to_owned(),
|
card_holder_name: card.card_holder_name.to_owned(),
|
||||||
@ -206,7 +208,7 @@ pub async fn save_payout_data_to_locker(
|
|||||||
card_type: None,
|
card_type: None,
|
||||||
};
|
};
|
||||||
let payload = StoreLockerReq::LockerCard(StoreCardReq {
|
let payload = StoreLockerReq::LockerCard(StoreCardReq {
|
||||||
merchant_id: &merchant_account.merchant_id,
|
merchant_id: merchant_account.merchant_id.as_ref(),
|
||||||
merchant_customer_id: payout_attempt.customer_id.to_owned(),
|
merchant_customer_id: payout_attempt.customer_id.to_owned(),
|
||||||
card: transformers::Card {
|
card: transformers::Card {
|
||||||
card_number: card.card_number.to_owned(),
|
card_number: card.card_number.to_owned(),
|
||||||
@ -250,31 +252,32 @@ pub async fn save_payout_data_to_locker(
|
|||||||
Ok(hex::encode(e.peek()))
|
Ok(hex::encode(e.peek()))
|
||||||
})?;
|
})?;
|
||||||
let payload = StoreLockerReq::LockerGeneric(StoreGenericReq {
|
let payload = StoreLockerReq::LockerGeneric(StoreGenericReq {
|
||||||
merchant_id: &merchant_account.merchant_id,
|
merchant_id: merchant_account.merchant_id.as_ref(),
|
||||||
merchant_customer_id: payout_attempt.customer_id.to_owned(),
|
merchant_customer_id: payout_attempt.customer_id.to_owned(),
|
||||||
enc_data,
|
enc_data,
|
||||||
});
|
});
|
||||||
match payout_method_data {
|
match payout_method_data {
|
||||||
api_models::payouts::PayoutMethodData::Bank(bank) => (
|
payouts::PayoutMethodData::Bank(bank) => (
|
||||||
payload,
|
payload,
|
||||||
None,
|
None,
|
||||||
Some(bank.to_owned()),
|
Some(bank.to_owned()),
|
||||||
None,
|
None,
|
||||||
api_enums::PaymentMethodType::foreign_from(bank.to_owned()),
|
api_enums::PaymentMethodType::foreign_from(bank.to_owned()),
|
||||||
),
|
),
|
||||||
api_models::payouts::PayoutMethodData::Wallet(wallet) => (
|
payouts::PayoutMethodData::Wallet(wallet) => (
|
||||||
payload,
|
payload,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
Some(wallet.to_owned()),
|
Some(wallet.to_owned()),
|
||||||
api_enums::PaymentMethodType::foreign_from(wallet.to_owned()),
|
api_enums::PaymentMethodType::foreign_from(wallet.to_owned()),
|
||||||
),
|
),
|
||||||
api_models::payouts::PayoutMethodData::Card(_) => {
|
payouts::PayoutMethodData::Card(_) => {
|
||||||
Err(errors::ApiErrorResponse::InternalServerError)?
|
Err(errors::ApiErrorResponse::InternalServerError)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Store payout method in locker
|
// Store payout method in locker
|
||||||
let stored_resp = cards::call_to_locker_hs(
|
let stored_resp = cards::call_to_locker_hs(
|
||||||
state,
|
state,
|
||||||
@ -285,8 +288,261 @@ pub async fn save_payout_data_to_locker(
|
|||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||||
|
|
||||||
// Store card_reference in payouts table
|
|
||||||
let db = &*state.store;
|
let db = &*state.store;
|
||||||
|
|
||||||
|
// Handle duplicates
|
||||||
|
let (should_insert_in_pm_table, metadata_update) = match stored_resp.duplication_check {
|
||||||
|
// Check if equivalent entry exists in payment_methods
|
||||||
|
Some(duplication_check) => {
|
||||||
|
let locker_ref = stored_resp.card_reference.clone();
|
||||||
|
|
||||||
|
// Use locker ref as payment_method_id
|
||||||
|
let existing_pm_by_pmid = db.find_payment_method(&locker_ref).await;
|
||||||
|
|
||||||
|
match existing_pm_by_pmid {
|
||||||
|
// If found, update locker's metadata [DELETE + INSERT OP], don't insert in payment_method's table
|
||||||
|
Ok(pm) => (
|
||||||
|
false,
|
||||||
|
if duplication_check == DataDuplicationCheck::MetaDataChanged {
|
||||||
|
Some(pm.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
// If not found, use locker ref as locker_id
|
||||||
|
Err(err) => {
|
||||||
|
if err.current_context().is_db_not_found() {
|
||||||
|
match db.find_payment_method_by_locker_id(&locker_ref).await {
|
||||||
|
// If found, update locker's metadata [DELETE + INSERT OP], don't insert in payment_methods table
|
||||||
|
Ok(pm) => (
|
||||||
|
false,
|
||||||
|
if duplication_check == DataDuplicationCheck::MetaDataChanged {
|
||||||
|
Some(pm.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Err(err) => {
|
||||||
|
// If not found, update locker's metadata [DELETE + INSERT OP], and insert in payment_methods table
|
||||||
|
if err.current_context().is_db_not_found() {
|
||||||
|
(true, None)
|
||||||
|
|
||||||
|
// Misc. DB errors
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
.change_context(
|
||||||
|
errors::ApiErrorResponse::InternalServerError,
|
||||||
|
)
|
||||||
|
.attach_printable(
|
||||||
|
"DB failures while finding payment method by locker ID",
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Misc. DB errors
|
||||||
|
} else {
|
||||||
|
Err(err)
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("DB failures while finding payment method by pm ID")?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not duplicate, should be inserted in payment_methods table
|
||||||
|
None => (true, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Form payment method entry and card's metadata whenever insertion or metadata update is required
|
||||||
|
let (card_details_encrypted, new_payment_method) =
|
||||||
|
if let (api::PayoutMethodData::Card(_), true, _)
|
||||||
|
| (api::PayoutMethodData::Card(_), _, Some(_)) = (
|
||||||
|
payout_method_data,
|
||||||
|
should_insert_in_pm_table,
|
||||||
|
metadata_update.as_ref(),
|
||||||
|
) {
|
||||||
|
// Fetch card info from db
|
||||||
|
let card_isin = card_details
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.card_number.clone().get_card_isin());
|
||||||
|
|
||||||
|
let mut payment_method = api::PaymentMethodCreate {
|
||||||
|
payment_method: api_enums::PaymentMethod::foreign_from(
|
||||||
|
payout_method_data.to_owned(),
|
||||||
|
),
|
||||||
|
payment_method_type: Some(payment_method_type),
|
||||||
|
payment_method_issuer: None,
|
||||||
|
payment_method_issuer_code: None,
|
||||||
|
bank_transfer: None,
|
||||||
|
card: card_details.clone(),
|
||||||
|
wallet: None,
|
||||||
|
metadata: None,
|
||||||
|
customer_id: Some(payout_attempt.customer_id.to_owned()),
|
||||||
|
card_network: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let pm_data = card_isin
|
||||||
|
.clone()
|
||||||
|
.async_and_then(|card_isin| async move {
|
||||||
|
db.get_card_info(&card_isin)
|
||||||
|
.await
|
||||||
|
.map_err(|error| services::logger::warn!(card_info_error=?error))
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.flatten()
|
||||||
|
.map(|card_info| {
|
||||||
|
payment_method.payment_method_issuer = card_info.card_issuer.clone();
|
||||||
|
payment_method.card_network =
|
||||||
|
card_info.card_network.clone().map(|cn| cn.to_string());
|
||||||
|
api::payment_methods::PaymentMethodsData::Card(
|
||||||
|
api::payment_methods::CardDetailsPaymentMethod {
|
||||||
|
last4_digits: card_details
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.card_number.clone().get_last4()),
|
||||||
|
issuer_country: card_info.card_issuing_country,
|
||||||
|
expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
|
||||||
|
expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
|
||||||
|
nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
|
||||||
|
card_holder_name: card_details
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| c.card_holder_name.clone()),
|
||||||
|
|
||||||
|
card_isin: card_isin.clone(),
|
||||||
|
card_issuer: card_info.card_issuer,
|
||||||
|
card_network: card_info.card_network,
|
||||||
|
card_type: card_info.card_type,
|
||||||
|
saved_to_locker: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
api::payment_methods::PaymentMethodsData::Card(
|
||||||
|
api::payment_methods::CardDetailsPaymentMethod {
|
||||||
|
last4_digits: card_details
|
||||||
|
.as_ref()
|
||||||
|
.map(|c| c.card_number.clone().get_last4()),
|
||||||
|
issuer_country: None,
|
||||||
|
expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
|
||||||
|
expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
|
||||||
|
nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
|
||||||
|
card_holder_name: card_details
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|c| c.card_holder_name.clone()),
|
||||||
|
|
||||||
|
card_isin: card_isin.clone(),
|
||||||
|
card_issuer: None,
|
||||||
|
card_network: None,
|
||||||
|
card_type: None,
|
||||||
|
saved_to_locker: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
(
|
||||||
|
cards::create_encrypted_payment_method_data(key_store, Some(pm_data)).await,
|
||||||
|
payment_method,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
api::PaymentMethodCreate {
|
||||||
|
payment_method: api_enums::PaymentMethod::foreign_from(
|
||||||
|
payout_method_data.to_owned(),
|
||||||
|
),
|
||||||
|
payment_method_type: Some(payment_method_type),
|
||||||
|
payment_method_issuer: None,
|
||||||
|
payment_method_issuer_code: None,
|
||||||
|
bank_transfer: bank_details,
|
||||||
|
card: None,
|
||||||
|
wallet: wallet_details,
|
||||||
|
metadata: None,
|
||||||
|
customer_id: Some(payout_attempt.customer_id.to_owned()),
|
||||||
|
card_network: None,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert new entry in payment_methods table
|
||||||
|
if should_insert_in_pm_table {
|
||||||
|
let payment_method_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm");
|
||||||
|
cards::create_payment_method(
|
||||||
|
db,
|
||||||
|
&new_payment_method,
|
||||||
|
&payout_attempt.customer_id,
|
||||||
|
&payment_method_id,
|
||||||
|
Some(stored_resp.card_reference.clone()),
|
||||||
|
&merchant_account.merchant_id,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
card_details_encrypted.clone(),
|
||||||
|
key_store,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 1. Delete from locker
|
||||||
|
* 2. Create new entry in locker
|
||||||
|
* 3. Handle creation response from locker
|
||||||
|
* 4. Update card's metadata in payment_methods table
|
||||||
|
*/
|
||||||
|
if let Some(existing_pm) = metadata_update {
|
||||||
|
let card_reference = &existing_pm
|
||||||
|
.locker_id
|
||||||
|
.clone()
|
||||||
|
.unwrap_or(existing_pm.payment_method_id.clone());
|
||||||
|
// Delete from locker
|
||||||
|
cards::delete_card_from_hs_locker(
|
||||||
|
state,
|
||||||
|
&payout_attempt.customer_id,
|
||||||
|
&merchant_account.merchant_id,
|
||||||
|
card_reference,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.attach_printable(
|
||||||
|
"Failed to delete PMD from locker as a part of metadata update operation",
|
||||||
|
)?;
|
||||||
|
|
||||||
|
locker_req.update_requestor_card_reference(Some(card_reference.to_string()));
|
||||||
|
|
||||||
|
// Store in locker
|
||||||
|
let stored_resp = cards::call_to_locker_hs(
|
||||||
|
state,
|
||||||
|
&locker_req,
|
||||||
|
&payout_attempt.customer_id,
|
||||||
|
api_enums::LockerChoice::HyperswitchCardVault,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError);
|
||||||
|
|
||||||
|
// Check if locker operation was successful or not, if not, delete the entry from payment_methods table
|
||||||
|
if let Err(err) = stored_resp {
|
||||||
|
logger::error!(vault_err=?err);
|
||||||
|
db.delete_payment_method_by_merchant_id_payment_method_id(
|
||||||
|
&merchant_account.merchant_id,
|
||||||
|
&existing_pm.payment_method_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.to_not_found_response(errors::ApiErrorResponse::PaymentMethodNotFound)?;
|
||||||
|
|
||||||
|
Err(errors::ApiErrorResponse::InternalServerError).attach_printable(
|
||||||
|
"Failed to insert PMD from locker as a part of metadata update operation",
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update card's metadata in payment_methods table
|
||||||
|
let pm_update = storage::PaymentMethodUpdate::PaymentMethodDataUpdate {
|
||||||
|
payment_method_data: card_details_encrypted,
|
||||||
|
};
|
||||||
|
db.update_payment_method(existing_pm, pm_update)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed to add payment method in db")?;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Store card_reference in payouts table
|
||||||
let updated_payout = storage::PayoutsUpdate::PayoutMethodIdUpdate {
|
let updated_payout = storage::PayoutsUpdate::PayoutMethodIdUpdate {
|
||||||
payout_method_id: stored_resp.card_reference.to_owned(),
|
payout_method_id: stored_resp.card_reference.to_owned(),
|
||||||
};
|
};
|
||||||
@ -299,100 +555,6 @@ pub async fn save_payout_data_to_locker(
|
|||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
.attach_printable("Error updating payouts in saved payout method")?;
|
.attach_printable("Error updating payouts in saved payout method")?;
|
||||||
|
|
||||||
// fetch card info from db
|
|
||||||
let card_isin = card_details
|
|
||||||
.as_ref()
|
|
||||||
.map(|c| c.card_number.clone().get_card_isin());
|
|
||||||
|
|
||||||
let pm_data = card_isin
|
|
||||||
.clone()
|
|
||||||
.async_and_then(|card_isin| async move {
|
|
||||||
db.get_card_info(&card_isin)
|
|
||||||
.await
|
|
||||||
.map_err(|error| services::logger::warn!(card_info_error=?error))
|
|
||||||
.ok()
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.flatten()
|
|
||||||
.map(|card_info| {
|
|
||||||
api::payment_methods::PaymentMethodsData::Card(
|
|
||||||
api::payment_methods::CardDetailsPaymentMethod {
|
|
||||||
last4_digits: card_details
|
|
||||||
.as_ref()
|
|
||||||
.map(|c| c.card_number.clone().get_last4()),
|
|
||||||
issuer_country: card_info.card_issuing_country,
|
|
||||||
expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
|
|
||||||
expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
|
|
||||||
nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
|
|
||||||
card_holder_name: card_details
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|c| c.card_holder_name.clone()),
|
|
||||||
|
|
||||||
card_isin: card_isin.clone(),
|
|
||||||
card_issuer: card_info.card_issuer,
|
|
||||||
card_network: card_info.card_network,
|
|
||||||
card_type: card_info.card_type,
|
|
||||||
saved_to_locker: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
api::payment_methods::PaymentMethodsData::Card(
|
|
||||||
api::payment_methods::CardDetailsPaymentMethod {
|
|
||||||
last4_digits: card_details
|
|
||||||
.as_ref()
|
|
||||||
.map(|c| c.card_number.clone().get_last4()),
|
|
||||||
issuer_country: None,
|
|
||||||
expiry_month: card_details.as_ref().map(|c| c.card_exp_month.clone()),
|
|
||||||
expiry_year: card_details.as_ref().map(|c| c.card_exp_year.clone()),
|
|
||||||
nick_name: card_details.as_ref().and_then(|c| c.nick_name.clone()),
|
|
||||||
card_holder_name: card_details
|
|
||||||
.as_ref()
|
|
||||||
.and_then(|c| c.card_holder_name.clone()),
|
|
||||||
|
|
||||||
card_isin: card_isin.clone(),
|
|
||||||
card_issuer: None,
|
|
||||||
card_network: None,
|
|
||||||
card_type: None,
|
|
||||||
saved_to_locker: true,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let card_details_encrypted =
|
|
||||||
cards::create_encrypted_payment_method_data(key_store, Some(pm_data)).await;
|
|
||||||
|
|
||||||
// Insert in payment_method table
|
|
||||||
let payment_method = api::PaymentMethodCreate {
|
|
||||||
payment_method: api_enums::PaymentMethod::foreign_from(payout_method_data.to_owned()),
|
|
||||||
payment_method_type: Some(payment_method_type),
|
|
||||||
payment_method_issuer: None,
|
|
||||||
payment_method_issuer_code: None,
|
|
||||||
bank_transfer: bank_details,
|
|
||||||
card: card_details,
|
|
||||||
wallet: wallet_details,
|
|
||||||
metadata: None,
|
|
||||||
customer_id: Some(payout_attempt.customer_id.to_owned()),
|
|
||||||
card_network: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let payment_method_id = common_utils::generate_id(crate::consts::ID_LENGTH, "pm");
|
|
||||||
cards::create_payment_method(
|
|
||||||
db,
|
|
||||||
&payment_method,
|
|
||||||
&payout_attempt.customer_id,
|
|
||||||
&payment_method_id,
|
|
||||||
Some(stored_resp.card_reference),
|
|
||||||
&merchant_account.merchant_id,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
card_details_encrypted,
|
|
||||||
key_store,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,7 +788,7 @@ pub fn should_call_payout_connector_create_customer<'a>(
|
|||||||
connector_label: &str,
|
connector_label: &str,
|
||||||
) -> (bool, Option<&'a str>) {
|
) -> (bool, Option<&'a str>) {
|
||||||
// Check if create customer is required for the connector
|
// Check if create customer is required for the connector
|
||||||
match PayoutConnectors::try_from(connector.connector_name) {
|
match enums::PayoutConnectors::try_from(connector.connector_name) {
|
||||||
Ok(connector) => {
|
Ok(connector) => {
|
||||||
let connector_needs_customer = state
|
let connector_needs_customer = state
|
||||||
.conf
|
.conf
|
||||||
|
|||||||
Reference in New Issue
Block a user