Skip to content

Commit

Permalink
fix(api_request): make payment_method_data as optional (#4527)
Browse files Browse the repository at this point in the history
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
  • Loading branch information
Narayanbhat166 and hyperswitch-bot[bot] authored May 3, 2024
1 parent ecc97bc commit 83a1924
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 70 deletions.
91 changes: 70 additions & 21 deletions crates/api_models/src/payments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1288,15 +1288,56 @@ mod payment_method_data_serde {
OptionalPaymentMethod(serde_json::Value),
}

// This struct is an intermediate representation
// This is required in order to catch deserialization errors when deserializing `payment_method_data`
// The #[serde(flatten)] attribute applied on `payment_method_data` discards
// any of the error when deserializing and deserializes to an option instead
#[derive(serde::Deserialize, Debug)]
struct __InnerPaymentMethodData {
billing: Option<Address>,
#[serde(flatten)]
payment_method_data: Option<serde_json::Value>,
}

let deserialize_to_inner = __Inner::deserialize(deserializer)?;

match deserialize_to_inner {
__Inner::OptionalPaymentMethod(value) => {
let parsed_value = serde_json::from_value::<PaymentMethodDataRequest>(value)
let parsed_value = serde_json::from_value::<__InnerPaymentMethodData>(value)
.map_err(|serde_json_error| {
serde::de::Error::custom(serde_json_error.to_string())
})?;

Ok(Some(parsed_value))
let payment_method_data = if let Some(payment_method_data_value) =
parsed_value.payment_method_data
{
// Even though no data is passed, the flatten serde_json::Value is deserialized as Some(Object {})
if let serde_json::Value::Object(ref inner_map) = payment_method_data_value {
if inner_map.is_empty() {
None
} else {
Some(
serde_json::from_value::<PaymentMethodData>(
payment_method_data_value,
)
.map_err(|serde_json_error| {
serde::de::Error::custom(serde_json_error.to_string())
})?,
)
}
} else {
Err(serde::de::Error::custom(
"Expected a map for payment_method_data",
))?
}
} else {
None
};

Ok(Some(PaymentMethodDataRequest {
payment_method_data,
billing: parsed_value.billing,
}))
}
__Inner::RewardString(inner_string) => {
let payment_method_data = match inner_string.as_str() {
Expand All @@ -1305,7 +1346,7 @@ mod payment_method_data_serde {
};

Ok(Some(PaymentMethodDataRequest {
payment_method_data,
payment_method_data: Some(payment_method_data),
billing: None,
}))
}
Expand All @@ -1320,21 +1361,29 @@ mod payment_method_data_serde {
S: Serializer,
{
if let Some(payment_method_data_request) = payment_method_data_request {
match payment_method_data_request.payment_method_data {
PaymentMethodData::Reward => serializer.serialize_str("reward"),
PaymentMethodData::CardRedirect(_)
| PaymentMethodData::BankDebit(_)
| PaymentMethodData::BankRedirect(_)
| PaymentMethodData::BankTransfer(_)
| PaymentMethodData::CardToken(_)
| PaymentMethodData::Crypto(_)
| PaymentMethodData::GiftCard(_)
| PaymentMethodData::PayLater(_)
| PaymentMethodData::Upi(_)
| PaymentMethodData::Voucher(_)
| PaymentMethodData::Card(_)
| PaymentMethodData::MandatePayment
| PaymentMethodData::Wallet(_) => payment_method_data_request.serialize(serializer),
if let Some(payment_method_data) =
payment_method_data_request.payment_method_data.as_ref()
{
match payment_method_data {
PaymentMethodData::Reward => serializer.serialize_str("reward"),
PaymentMethodData::CardRedirect(_)
| PaymentMethodData::BankDebit(_)
| PaymentMethodData::BankRedirect(_)
| PaymentMethodData::BankTransfer(_)
| PaymentMethodData::CardToken(_)
| PaymentMethodData::Crypto(_)
| PaymentMethodData::GiftCard(_)
| PaymentMethodData::PayLater(_)
| PaymentMethodData::Upi(_)
| PaymentMethodData::Voucher(_)
| PaymentMethodData::Card(_)
| PaymentMethodData::MandatePayment
| PaymentMethodData::Wallet(_) => {
payment_method_data_request.serialize(serializer)
}
}
} else {
payment_method_data_request.serialize(serializer)
}
} else {
serializer.serialize_none()
Expand All @@ -1345,7 +1394,7 @@ mod payment_method_data_serde {
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, ToSchema, Eq, PartialEq)]
pub struct PaymentMethodDataRequest {
#[serde(flatten)]
pub payment_method_data: PaymentMethodData,
pub payment_method_data: Option<PaymentMethodData>,
/// billing details for the payment method.
/// This billing details will be passed to the processor as billing address.
/// If not passed, then payment.billing will be considered
Expand Down Expand Up @@ -4715,7 +4764,7 @@ mod payments_request_api_contract {
let payments_request = serde_json::from_str::<PaymentsRequest>(payments_request);
assert!(payments_request.is_ok());

if let PaymentMethodData::Card(card_data) = payments_request
if let Some(PaymentMethodData::Card(card_data)) = payments_request
.unwrap()
.payment_method_data
.unwrap()
Expand Down Expand Up @@ -4747,7 +4796,7 @@ mod payments_request_api_contract {
.payment_method_data
.unwrap()
.payment_method_data,
PaymentMethodData::Reward
Some(PaymentMethodData::Reward)
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,9 @@ impl TryFrom<StripePaymentIntentRequest> for payments::PaymentsRequest {
pmd.payment_method_details
.as_ref()
.map(|spmd| payments::PaymentMethodDataRequest {
payment_method_data: payments::PaymentMethodData::from(spmd.to_owned()),
payment_method_data: Some(payments::PaymentMethodData::from(
spmd.to_owned(),
)),
billing: pmd.billing_details.clone().map(payments::Address::from),
})
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ impl TryFrom<StripeSetupIntentRequest> for payments::PaymentsRequest {
pmd.payment_method_details
.as_ref()
.map(|spmd| payments::PaymentMethodDataRequest {
payment_method_data: payments::PaymentMethodData::from(spmd.to_owned()),
payment_method_data: Some(payments::PaymentMethodData::from(
spmd.to_owned(),
)),
billing: pmd.billing_details.clone().map(payments::Address::from),
})
}),
Expand Down
38 changes: 26 additions & 12 deletions crates/router/src/core/payments/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1159,7 +1159,7 @@ pub fn verify_mandate_details_for_recurring_payments(

#[instrument(skip_all)]
pub fn payment_attempt_status_fsm(
payment_method_data: &Option<api::payments::PaymentMethodDataRequest>,
payment_method_data: Option<&api::payments::PaymentMethodData>,
confirm: Option<bool>,
) -> storage_enums::AttemptStatus {
match payment_method_data {
Expand All @@ -1172,7 +1172,7 @@ pub fn payment_attempt_status_fsm(
}

pub fn payment_intent_status_fsm(
payment_method_data: &Option<api::PaymentMethodDataRequest>,
payment_method_data: Option<&api::PaymentMethodData>,
confirm: Option<bool>,
) -> storage_enums::IntentStatus {
match payment_method_data {
Expand Down Expand Up @@ -2129,8 +2129,14 @@ pub(crate) fn validate_amount_to_capture(
pub(crate) fn validate_payment_method_fields_present(
req: &api::PaymentsRequest,
) -> RouterResult<()> {
let payment_method_data =
req.payment_method_data
.as_ref()
.and_then(|request_payment_method_data| {
request_payment_method_data.payment_method_data.as_ref()
});
utils::when(
req.payment_method.is_none() && req.payment_method_data.is_some(),
req.payment_method.is_none() && payment_method_data.is_some(),
|| {
Err(errors::ApiErrorResponse::MissingRequiredField {
field_name: "payment_method",
Expand All @@ -2152,7 +2158,7 @@ pub(crate) fn validate_payment_method_fields_present(

utils::when(
req.payment_method.is_some()
&& req.payment_method_data.is_none()
&& payment_method_data.is_none()
&& req.payment_token.is_none()
&& req.recurring_details.is_none(),
|| {
Expand Down Expand Up @@ -2194,14 +2200,14 @@ pub(crate) fn validate_payment_method_fields_present(
};

utils::when(
req.payment_method.is_some() && req.payment_method_data.is_some(),
req.payment_method.is_some() && payment_method_data.is_some(),
|| {
req.payment_method_data
.clone()
.map_or(Ok(()), |req_payment_method_data| {
payment_method_data
.cloned()
.map_or(Ok(()), |payment_method_data| {
req.payment_method.map_or(Ok(()), |req_payment_method| {
validate_payment_method_and_payment_method_data(
req_payment_method_data.payment_method_data,
payment_method_data,
req_payment_method,
)
})
Expand Down Expand Up @@ -3409,7 +3415,7 @@ impl AttemptType {
// In case if fields are not overridden by the request then they contain the same data that was in the previous attempt provided it is populated in this function.
#[inline(always)]
fn make_new_payment_attempt(
payment_method_data: &Option<api_models::payments::PaymentMethodDataRequest>,
payment_method_data: Option<&api_models::payments::PaymentMethodData>,
old_payment_attempt: PaymentAttempt,
new_attempt_count: i16,
storage_scheme: enums::MerchantStorageScheme,
Expand Down Expand Up @@ -3507,7 +3513,11 @@ impl AttemptType {
let new_payment_attempt = db
.insert_payment_attempt(
Self::make_new_payment_attempt(
&request.payment_method_data,
request.payment_method_data.as_ref().and_then(
|request_payment_method_data| {
request_payment_method_data.payment_method_data.as_ref()
},
),
fetched_payment_attempt,
new_attempt_count,
storage_scheme,
Expand All @@ -3524,7 +3534,11 @@ impl AttemptType {
fetched_payment_intent,
storage::PaymentIntentUpdate::StatusAndAttemptUpdate {
status: payment_intent_status_fsm(
&request.payment_method_data,
request.payment_method_data.as_ref().and_then(
|request_payment_method_data| {
request_payment_method_data.payment_method_data.as_ref()
},
),
Some(true),
),
active_attempt_id: new_payment_attempt.attempt_id.clone(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
&request
.payment_method_data
.as_ref()
.map(|pmd| pmd.payment_method_data.clone()),
.and_then(|pmd| pmd.payment_method_data.clone()),
&request.payment_method_type,
&mandate_type,
&token,
Expand Down Expand Up @@ -284,7 +284,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
payment_method_data: request
.payment_method_data
.as_ref()
.map(|pmd| pmd.payment_method_data.clone()),
.and_then(|pmd| pmd.payment_method_data.clone()),
payment_method_info,
force_sync: None,
refunds: vec![],
Expand Down
13 changes: 7 additions & 6 deletions crates/router/src/core/payments/operations/payment_confirm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
request
.payment_method_data
.as_ref()
.map(|pmd| pmd.payment_method_data.clone()),
.and_then(|pmd| pmd.payment_method_data.clone()),
)?;

payment_attempt.browser_info = browser_info;
Expand Down Expand Up @@ -412,7 +412,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
let n_request_payment_method_data = request
.payment_method_data
.as_ref()
.map(|pmd| pmd.payment_method_data.clone());
.and_then(|pmd| pmd.payment_method_data.clone());

let store = state.clone().store;

Expand Down Expand Up @@ -524,7 +524,7 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
&request
.payment_method_data
.as_ref()
.map(|pmd| pmd.payment_method_data.clone()),
.and_then(|pmd| pmd.payment_method_data.clone()),
&request.payment_method_type,
&mandate_type,
&token,
Expand Down Expand Up @@ -570,11 +570,12 @@ impl<F: Send + Clone, Ctx: PaymentMethodRetrieve>
let payment_method_data_after_card_bin_call = request
.payment_method_data
.as_ref()
.and_then(|request_payment_method_data| {
request_payment_method_data.payment_method_data.as_ref()
})
.zip(additional_pm_data)
.map(|(payment_method_data, additional_payment_data)| {
payment_method_data
.payment_method_data
.apply_additional_payment_data(additional_payment_data)
payment_method_data.apply_additional_payment_data(additional_payment_data)
});
let authentication = payment_attempt.authentication_id.as_ref().async_map(|authentication_id| async move {
state
Expand Down
Loading

0 comments on commit 83a1924

Please sign in to comment.