mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 04:04:43 +08:00
fix(router): Add additional card info in payment response (#1745)
Co-authored-by: Sahkal Poddar <sahkal.poddar@juspay.in>
This commit is contained in:
@ -6,7 +6,7 @@ use common_utils::{
|
||||
ext_traits::Encode,
|
||||
pii::{self, Email},
|
||||
};
|
||||
use masking::{PeekInterface, Secret};
|
||||
use masking::Secret;
|
||||
use router_derive::Setter;
|
||||
use time::PrimitiveDateTime;
|
||||
use url::Url;
|
||||
@ -723,16 +723,25 @@ pub enum PaymentMethodData {
|
||||
Upi(UpiData),
|
||||
}
|
||||
|
||||
#[derive(Default, Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub struct AdditionalCardInfo {
|
||||
pub card_issuer: Option<String>,
|
||||
pub card_network: Option<api_enums::CardNetwork>,
|
||||
pub card_type: Option<String>,
|
||||
pub card_issuing_country: Option<String>,
|
||||
pub bank_code: Option<String>,
|
||||
pub last4: String,
|
||||
pub card_isin: String,
|
||||
pub card_exp_month: Secret<String>,
|
||||
pub card_exp_year: Secret<String>,
|
||||
pub card_holder_name: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AdditionalPaymentData {
|
||||
Card {
|
||||
card_issuer: Option<String>,
|
||||
card_network: Option<api_enums::CardNetwork>,
|
||||
card_type: Option<String>,
|
||||
card_issuing_country: Option<String>,
|
||||
bank_code: Option<String>,
|
||||
},
|
||||
Card(Box<AdditionalCardInfo>),
|
||||
BankRedirect {
|
||||
bank_name: Option<api_enums::BankNames>,
|
||||
},
|
||||
@ -1136,11 +1145,17 @@ pub struct ApplepayPaymentMethod {
|
||||
pub pm_type: String,
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Serialize)]
|
||||
#[derive(Eq, PartialEq, Clone, Debug, serde::Serialize, serde::Deserialize)]
|
||||
pub struct CardResponse {
|
||||
last4: String,
|
||||
exp_month: String,
|
||||
exp_year: String,
|
||||
pub last4: String,
|
||||
pub card_type: Option<String>,
|
||||
pub card_network: Option<api_enums::CardNetwork>,
|
||||
pub card_issuer: Option<String>,
|
||||
pub card_issuing_country: Option<String>,
|
||||
pub card_isin: String,
|
||||
pub card_exp_month: Secret<String>,
|
||||
pub card_exp_year: Secret<String>,
|
||||
pub card_holder_name: Secret<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
@ -1150,21 +1165,21 @@ pub struct RewardData {
|
||||
pub merchant_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum PaymentMethodDataResponse {
|
||||
#[serde(rename = "card")]
|
||||
Card(CardResponse),
|
||||
BankTransfer(BankTransferData),
|
||||
Wallet(WalletData),
|
||||
PayLater(PayLaterData),
|
||||
BankTransfer,
|
||||
Wallet,
|
||||
PayLater,
|
||||
Paypal,
|
||||
BankRedirect(BankRedirectData),
|
||||
Crypto(CryptoData),
|
||||
BankDebit(BankDebitData),
|
||||
BankRedirect,
|
||||
Crypto,
|
||||
BankDebit,
|
||||
MandatePayment,
|
||||
Reward(RewardData),
|
||||
Upi(UpiData),
|
||||
Reward,
|
||||
Upi,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, ToSchema)]
|
||||
@ -1802,35 +1817,35 @@ impl From<PaymentsStartRequest> for PaymentsRequest {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Card> for CardResponse {
|
||||
fn from(card: Card) -> Self {
|
||||
let card_number_length = card.card_number.peek().clone().len();
|
||||
impl From<AdditionalCardInfo> for CardResponse {
|
||||
fn from(card: AdditionalCardInfo) -> Self {
|
||||
Self {
|
||||
last4: card.card_number.peek().clone()[card_number_length - 4..card_number_length]
|
||||
.to_string(),
|
||||
exp_month: card.card_exp_month.peek().clone(),
|
||||
exp_year: card.card_exp_year.peek().clone(),
|
||||
last4: card.last4,
|
||||
card_type: card.card_type,
|
||||
card_network: card.card_network,
|
||||
card_issuer: card.card_issuer,
|
||||
card_issuing_country: card.card_issuing_country,
|
||||
card_isin: card.card_isin,
|
||||
card_exp_month: card.card_exp_month,
|
||||
card_exp_year: card.card_exp_year,
|
||||
card_holder_name: card.card_holder_name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PaymentMethodData> for PaymentMethodDataResponse {
|
||||
fn from(payment_method_data: PaymentMethodData) -> Self {
|
||||
impl From<AdditionalPaymentData> for PaymentMethodDataResponse {
|
||||
fn from(payment_method_data: AdditionalPaymentData) -> Self {
|
||||
match payment_method_data {
|
||||
PaymentMethodData::Card(card) => Self::Card(CardResponse::from(card)),
|
||||
PaymentMethodData::PayLater(pay_later_data) => Self::PayLater(pay_later_data),
|
||||
PaymentMethodData::Wallet(wallet_data) => Self::Wallet(wallet_data),
|
||||
PaymentMethodData::BankRedirect(bank_redirect_data) => {
|
||||
Self::BankRedirect(bank_redirect_data)
|
||||
}
|
||||
PaymentMethodData::BankTransfer(bank_transfer_data) => {
|
||||
Self::BankTransfer(*bank_transfer_data)
|
||||
}
|
||||
PaymentMethodData::Crypto(crpto_data) => Self::Crypto(crpto_data),
|
||||
PaymentMethodData::BankDebit(bank_debit_data) => Self::BankDebit(bank_debit_data),
|
||||
PaymentMethodData::MandatePayment => Self::MandatePayment,
|
||||
PaymentMethodData::Reward(reward_data) => Self::Reward(reward_data),
|
||||
PaymentMethodData::Upi(upi_data) => Self::Upi(upi_data),
|
||||
AdditionalPaymentData::Card(card) => Self::Card(CardResponse::from(*card)),
|
||||
AdditionalPaymentData::PayLater {} => Self::PayLater,
|
||||
AdditionalPaymentData::Wallet {} => Self::Wallet,
|
||||
AdditionalPaymentData::BankRedirect { .. } => Self::BankRedirect,
|
||||
AdditionalPaymentData::Crypto {} => Self::Crypto,
|
||||
AdditionalPaymentData::BankDebit {} => Self::BankDebit,
|
||||
AdditionalPaymentData::MandatePayment {} => Self::MandatePayment,
|
||||
AdditionalPaymentData::Reward {} => Self::Reward,
|
||||
AdditionalPaymentData::Upi {} => Self::Upi,
|
||||
AdditionalPaymentData::BankTransfer {} => Self::BankTransfer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,6 +22,17 @@ impl CardNumber {
|
||||
pub fn get_card_isin(self) -> String {
|
||||
self.0.peek().chars().take(6).collect::<String>()
|
||||
}
|
||||
pub fn get_last4(self) -> String {
|
||||
self.0
|
||||
.peek()
|
||||
.chars()
|
||||
.rev()
|
||||
.take(4)
|
||||
.collect::<String>()
|
||||
.chars()
|
||||
.rev()
|
||||
.collect::<String>()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CardNumber {
|
||||
|
||||
@ -2506,43 +2506,65 @@ pub async fn get_additional_payment_data(
|
||||
) -> api_models::payments::AdditionalPaymentData {
|
||||
match pm_data {
|
||||
api_models::payments::PaymentMethodData::Card(card_data) => {
|
||||
let card_isin = card_data.card_number.clone().get_card_isin();
|
||||
let last4 = card_data.card_number.clone().get_last4();
|
||||
if card_data.card_issuer.is_some()
|
||||
&& card_data.card_network.is_some()
|
||||
&& card_data.card_type.is_some()
|
||||
&& card_data.card_issuing_country.is_some()
|
||||
&& card_data.bank_code.is_some()
|
||||
{
|
||||
api_models::payments::AdditionalPaymentData::Card {
|
||||
api_models::payments::AdditionalPaymentData::Card(Box::new(
|
||||
api_models::payments::AdditionalCardInfo {
|
||||
card_issuer: card_data.card_issuer.to_owned(),
|
||||
card_network: card_data.card_network.clone(),
|
||||
card_type: card_data.card_type.to_owned(),
|
||||
card_issuing_country: card_data.card_issuing_country.to_owned(),
|
||||
bank_code: card_data.bank_code.to_owned(),
|
||||
}
|
||||
card_exp_month: card_data.card_exp_month.clone(),
|
||||
card_exp_year: card_data.card_exp_year.clone(),
|
||||
card_holder_name: card_data.card_holder_name.clone(),
|
||||
last4: last4.clone(),
|
||||
card_isin: card_isin.clone(),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
let card_number = card_data.clone().card_number;
|
||||
let card_info = db
|
||||
.get_card_info(&card_number.get_card_isin())
|
||||
.get_card_info(&card_isin.clone())
|
||||
.await
|
||||
.map_err(|error| services::logger::warn!(card_info_error=?error))
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(
|
||||
|card_info| api_models::payments::AdditionalPaymentData::Card {
|
||||
.map(|card_info| {
|
||||
api_models::payments::AdditionalPaymentData::Card(Box::new(
|
||||
api_models::payments::AdditionalCardInfo {
|
||||
card_issuer: card_info.card_issuer,
|
||||
card_network: card_info.card_network.clone(),
|
||||
bank_code: card_info.bank_code,
|
||||
card_type: card_info.card_type,
|
||||
card_issuing_country: card_info.card_issuing_country,
|
||||
last4: last4.clone(),
|
||||
card_isin: card_isin.clone(),
|
||||
card_exp_month: card_data.card_exp_month.clone(),
|
||||
card_exp_year: card_data.card_exp_year.clone(),
|
||||
card_holder_name: card_data.card_holder_name.clone(),
|
||||
},
|
||||
);
|
||||
card_info.unwrap_or(api_models::payments::AdditionalPaymentData::Card {
|
||||
))
|
||||
});
|
||||
card_info.unwrap_or(api_models::payments::AdditionalPaymentData::Card(Box::new(
|
||||
api_models::payments::AdditionalCardInfo {
|
||||
card_issuer: None,
|
||||
card_network: None,
|
||||
bank_code: None,
|
||||
card_type: None,
|
||||
card_issuing_country: None,
|
||||
})
|
||||
last4,
|
||||
card_isin,
|
||||
card_exp_month: card_data.card_exp_month.clone(),
|
||||
card_exp_year: card_data.card_exp_year.clone(),
|
||||
card_holder_name: card_data.card_holder_name.clone(),
|
||||
},
|
||||
)))
|
||||
}
|
||||
}
|
||||
api_models::payments::PaymentMethodData::BankRedirect(bank_redirect_data) => {
|
||||
|
||||
@ -232,6 +232,17 @@ where
|
||||
_server: &Server,
|
||||
_operation: Op,
|
||||
) -> RouterResponse<Self> {
|
||||
let additional_payment_method_data: Option<api_models::payments::AdditionalPaymentData> =
|
||||
data.payment_attempt
|
||||
.payment_method_data
|
||||
.clone()
|
||||
.map(|data| data.parse_value("payment_method_data"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "payment_method_data",
|
||||
})?;
|
||||
let payment_method_data_response =
|
||||
additional_payment_method_data.map(api::PaymentMethodDataResponse::from);
|
||||
Ok(services::ApplicationResponse::Json(Self {
|
||||
verify_id: Some(data.payment_intent.payment_id),
|
||||
merchant_id: Some(data.payment_intent.merchant_id),
|
||||
@ -248,9 +259,7 @@ where
|
||||
.and_then(|cus| cus.phone.as_ref().map(|s| s.to_owned())),
|
||||
mandate_id: data.mandate_id.map(|mandate_ids| mandate_ids.mandate_id),
|
||||
payment_method: data.payment_attempt.payment_method,
|
||||
payment_method_data: data
|
||||
.payment_method_data
|
||||
.map(api::PaymentMethodDataResponse::from),
|
||||
payment_method_data: payment_method_data_response,
|
||||
payment_token: data.token,
|
||||
error_code: data.payment_attempt.error_code,
|
||||
error_message: data.payment_attempt.error_message,
|
||||
@ -325,6 +334,18 @@ where
|
||||
.as_ref()
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or("".to_owned());
|
||||
let additional_payment_method_data: Option<api_models::payments::AdditionalPaymentData> =
|
||||
payment_attempt
|
||||
.payment_method_data
|
||||
.clone()
|
||||
.map(|data| data.parse_value("payment_method_data"))
|
||||
.transpose()
|
||||
.change_context(errors::ApiErrorResponse::InvalidDataValue {
|
||||
field_name: "payment_method_data",
|
||||
})?;
|
||||
|
||||
let payment_method_data_response =
|
||||
additional_payment_method_data.map(api::PaymentMethodDataResponse::from);
|
||||
|
||||
let output = Ok(match payment_request {
|
||||
Some(_request) => {
|
||||
@ -437,7 +458,7 @@ where
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_payment_method_data(
|
||||
payment_method_data.map(api::PaymentMethodDataResponse::from),
|
||||
payment_method_data_response,
|
||||
auth_flow == services::AuthFlow::Merchant,
|
||||
)
|
||||
.set_payment_token(payment_attempt.payment_token)
|
||||
@ -496,7 +517,7 @@ where
|
||||
capture_method: payment_attempt.capture_method,
|
||||
error_message: payment_attempt.error_message,
|
||||
error_code: payment_attempt.error_code,
|
||||
payment_method_data: payment_method_data.map(api::PaymentMethodDataResponse::from),
|
||||
payment_method_data: payment_method_data_response,
|
||||
email: customer
|
||||
.as_ref()
|
||||
.and_then(|cus| cus.email.as_ref().map(|s| s.to_owned())),
|
||||
|
||||
Reference in New Issue
Block a user