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