Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(payment_methods): add support for passing card_cvc in payment_method_data object along with token #3024

Merged
merged 11 commits into from
Dec 4, 2023
Merged
11 changes: 8 additions & 3 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,9 @@ pub struct PaymentsRequest {
#[schema(example = "187282ab-40ef-47a9-9206-5099ba31e432")]
pub payment_token: Option<String>,

/// This is used when payment is to be confirmed and the card is not saved
#[schema(value_type = Option<String>)]
/// This is used when payment is to be confirmed and the card is not saved.
/// This field will be deprecated soon, use the CardToken object instead
#[schema(value_type = Option<String>, deprecated)]
pub card_cvc: Option<Secret<String>>,

/// The shipping address for the payment
Expand Down Expand Up @@ -720,12 +721,16 @@ pub struct Card {
pub nick_name: Option<Secret<String>>,
}

#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema)]
#[derive(Eq, PartialEq, Debug, serde::Deserialize, serde::Serialize, Clone, ToSchema, Default)]
#[serde(rename_all = "snake_case")]
pub struct CardToken {
/// The card holder's name
#[schema(value_type = String, example = "John Test")]
pub card_holder_name: Option<Secret<String>>,

/// The CVC number for the card
#[schema(value_type = Option<String>)]
pub card_cvc: Option<Secret<String>>,
}

#[derive(Eq, PartialEq, Clone, Debug, serde::Deserialize, serde::Serialize, ToSchema)]
Expand Down
6 changes: 0 additions & 6 deletions crates/router/src/core/payment_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ pub trait PaymentMethodRetrieve {
key_store: &domain::MerchantKeyStore,
token: &storage::PaymentTokenData,
payment_intent: &PaymentIntent,
card_cvc: Option<masking::Secret<String>>,
card_token_data: Option<&CardToken>,
) -> RouterResult<Option<(payments::PaymentMethodData, enums::PaymentMethod)>>;
}
Expand Down Expand Up @@ -126,7 +125,6 @@ impl PaymentMethodRetrieve for Oss {
merchant_key_store: &domain::MerchantKeyStore,
token_data: &storage::PaymentTokenData,
payment_intent: &PaymentIntent,
card_cvc: Option<masking::Secret<String>>,
card_token_data: Option<&CardToken>,
) -> RouterResult<Option<(payments::PaymentMethodData, enums::PaymentMethod)>> {
match token_data {
Expand All @@ -135,7 +133,6 @@ impl PaymentMethodRetrieve for Oss {
state,
&generic_token.token,
payment_intent,
card_cvc,
merchant_key_store,
card_token_data,
)
Expand All @@ -147,7 +144,6 @@ impl PaymentMethodRetrieve for Oss {
state,
&generic_token.token,
payment_intent,
card_cvc,
merchant_key_store,
card_token_data,
)
Expand All @@ -159,7 +155,6 @@ impl PaymentMethodRetrieve for Oss {
state,
&card_token.token,
payment_intent,
card_cvc,
card_token_data,
)
.await
Expand All @@ -171,7 +166,6 @@ impl PaymentMethodRetrieve for Oss {
state,
&card_token.token,
payment_intent,
card_cvc,
card_token_data,
)
.await
Expand Down
43 changes: 28 additions & 15 deletions crates/router/src/core/payments/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1354,7 +1354,6 @@ pub async fn retrieve_payment_method_with_temporary_token(
state: &AppState,
token: &str,
payment_intent: &PaymentIntent,
card_cvc: Option<masking::Secret<String>>,
merchant_key_store: &domain::MerchantKeyStore,
card_token_data: Option<&CardToken>,
) -> RouterResult<Option<(api::PaymentMethodData, enums::PaymentMethod)>> {
Expand Down Expand Up @@ -1395,10 +1394,13 @@ pub async fn retrieve_payment_method_with_temporary_token(
updated_card.card_holder_name = name_on_card;
}

if let Some(cvc) = card_cvc {
is_card_updated = true;
updated_card.card_cvc = cvc;
if let Some(token_data) = card_token_data {
if let Some(cvc) = token_data.card_cvc.clone() {
is_card_updated = true;
updated_card.card_cvc = cvc;
}
}

if is_card_updated {
let updated_pm = api::PaymentMethodData::Card(updated_card);
vault::Vault::store_payment_method_data_in_locker(
Expand Down Expand Up @@ -1444,7 +1446,6 @@ pub async fn retrieve_card_with_permanent_token(
state: &AppState,
token: &str,
payment_intent: &PaymentIntent,
card_cvc: Option<masking::Secret<String>>,
card_token_data: Option<&CardToken>,
) -> RouterResult<api::PaymentMethodData> {
let customer_id = payment_intent
Expand Down Expand Up @@ -1479,7 +1480,11 @@ pub async fn retrieve_card_with_permanent_token(
card_holder_name: name_on_card.unwrap_or(masking::Secret::from("".to_string())),
card_exp_month: card.card_exp_month,
card_exp_year: card.card_exp_year,
card_cvc: card_cvc.unwrap_or_default(),
card_cvc: card_token_data
.cloned()
.unwrap_or_default()
.card_cvc
.unwrap_or_default(),
SanchithHegde marked this conversation as resolved.
Show resolved Hide resolved
card_issuer: card.card_brand,
nick_name: card.nick_name.map(masking::Secret::new),
card_network: None,
Expand All @@ -1501,6 +1506,22 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>(
Option<api::PaymentMethodData>,
)> {
let request = &payment_data.payment_method_data.clone();

let mut card_token_data = payment_data
.payment_method_data
.clone()
.and_then(|pmd| match pmd {
api_models::payments::PaymentMethodData::CardToken(token_data) => Some(token_data),
_ => None,
})
.or(Some(CardToken::default()));

if let Some(cvc) = payment_data.card_cvc.clone() {
if let Some(token_data) = card_token_data.as_mut() {
token_data.card_cvc = Some(cvc);
}
}

let token = payment_data.token.clone();

let hyperswitch_token = match payment_data.mandate_id {
Expand Down Expand Up @@ -1560,13 +1581,6 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>(
}
};

let card_cvc = payment_data.card_cvc.clone();

let card_token_data = request.as_ref().and_then(|pmd| match pmd {
api_models::payments::PaymentMethodData::CardToken(token_data) => Some(token_data),
_ => None,
});

// TODO: Handle case where payment method and token both are present in request properly.
let payment_method = match (request, hyperswitch_token) {
(_, Some(hyperswitch_token)) => {
Expand All @@ -1575,8 +1589,7 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>(
merchant_key_store,
&hyperswitch_token,
&payment_data.payment_intent,
card_cvc,
card_token_data,
card_token_data.as_ref(),
)
.await
.attach_printable("in 'make_pm_data'")?;
Expand Down
11 changes: 9 additions & 2 deletions openapi/openapi_spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -4316,6 +4316,11 @@
"type": "string",
"description": "The card holder's name",
"example": "John Test"
},
"card_cvc": {
"type": "string",
"description": "The CVC number for the card",
"nullable": true
}
}
},
Expand Down Expand Up @@ -9545,7 +9550,8 @@
},
"card_cvc": {
"type": "string",
"description": "This is used when payment is to be confirmed and the card is not saved",
"description": "This is used when payment is to be confirmed and the card is not saved.\nThis field will be deprecated soon, use the CardToken object instead",
"deprecated": true,
"nullable": true
},
"shipping": {
Expand Down Expand Up @@ -9914,7 +9920,8 @@
},
"card_cvc": {
"type": "string",
"description": "This is used when payment is to be confirmed and the card is not saved",
"description": "This is used when payment is to be confirmed and the card is not saved.\nThis field will be deprecated soon, use the CardToken object instead",
"deprecated": true,
"nullable": true
},
"shipping": {
Expand Down
Loading