feat(connectors): [Stripe] add extended authorization for cards (#9084)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
AkshayaFoiger
2025-08-29 16:29:53 +05:30
committed by GitHub
parent 60ef06e8ff
commit 8cfa966d34
2 changed files with 119 additions and 15 deletions

View File

@ -2,7 +2,10 @@ use std::{collections::HashMap, fmt::Debug, ops::Deref};
use api_models::{self, enums as api_enums, payments};
use common_enums::{enums, AttemptStatus, PaymentChargeType, StripeChargeType};
use common_types::payments::{AcceptanceType, SplitPaymentsRequest};
use common_types::{
payments::{AcceptanceType, SplitPaymentsRequest},
primitive_wrappers,
};
use common_utils::{
collect_missing_value_keys,
errors::CustomResult,
@ -19,7 +22,7 @@ use hyperswitch_domain_models::{
},
router_data::{
AdditionalPaymentMethodConnectorResponse, ConnectorAuthType, ConnectorResponseData,
PaymentMethodToken, RouterData,
ExtendedAuthorizationResponseData, PaymentMethodToken, RouterData,
},
router_flow_types::{Execute, RSync},
router_request_types::{
@ -287,6 +290,9 @@ pub struct StripeCardData {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "payment_method_options[card][request_incremental_authorization]")]
pub request_incremental_authorization: Option<StripeRequestIncrementalAuthorization>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "payment_method_options[card][request_extended_authorization]")]
request_extended_authorization: Option<StripeRequestExtendedAuthorization>,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
@ -296,6 +302,12 @@ pub enum StripeRequestIncrementalAuthorization {
Never,
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize, Clone)]
#[serde(rename_all = "snake_case")]
pub enum StripeRequestExtendedAuthorization {
IfAvailable,
}
#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct StripePayLaterData {
#[serde(rename = "payment_method_data[type]")]
@ -1260,6 +1272,7 @@ fn create_stripe_payment_method(
is_customer_initiated_mandate_payment: Option<bool>,
billing_address: StripeBillingAddress,
request_incremental_authorization: bool,
request_extended_authorization: Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
) -> Result<
(
StripePaymentMethodData,
@ -1279,6 +1292,7 @@ fn create_stripe_payment_method(
card_details,
payment_method_auth_type,
request_incremental_authorization,
request_extended_authorization,
))?,
Some(StripePaymentMethodType::Card),
billing_address,
@ -1497,10 +1511,27 @@ fn get_stripe_card_network(card_network: common_enums::CardNetwork) -> Option<St
}
}
impl TryFrom<(&Card, Auth3ds, bool)> for StripePaymentMethodData {
impl
TryFrom<(
&Card,
Auth3ds,
bool,
Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
)> for StripePaymentMethodData
{
type Error = ConnectorError;
fn try_from(
(card, payment_method_auth_type, request_incremental_authorization): (&Card, Auth3ds, bool),
(
card,
payment_method_auth_type,
request_incremental_authorization,
request_extended_authorization,
): (
&Card,
Auth3ds,
bool,
Option<primitive_wrappers::RequestExtendedAuthorizationBool>,
),
) -> Result<Self, Self::Error> {
Ok(Self::Card(StripeCardData {
payment_method_data_type: StripePaymentMethodType::Card,
@ -1518,6 +1549,14 @@ impl TryFrom<(&Card, Auth3ds, bool)> for StripePaymentMethodData {
} else {
None
},
request_extended_authorization: if request_extended_authorization
.map(|request_extended_authorization| request_extended_authorization.is_true())
.unwrap_or(false)
{
Some(StripeRequestExtendedAuthorization::IfAvailable)
} else {
None
},
}))
}
}
@ -1868,6 +1907,7 @@ impl TryFrom<(&PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntentRequest
.clone()
.and_then(get_stripe_card_network),
request_incremental_authorization: None,
request_extended_authorization: None,
}),
PaymentMethodData::CardRedirect(_)
| PaymentMethodData::Wallet(_)
@ -1917,6 +1957,7 @@ impl TryFrom<(&PaymentsAuthorizeRouterData, MinorUnit)> for PaymentIntentRequest
}
})?,
item.request.request_incremental_authorization,
item.request.request_extended_authorization,
)?;
validate_shipping_address_against_payment_method(
@ -2243,6 +2284,7 @@ impl TryFrom<&TokenizationRouterData> for TokenRequest {
None,
StripeBillingAddress::default(),
false,
None,
)?
.0
}
@ -2537,6 +2579,21 @@ pub struct StripeAdditionalCardDetails {
checks: Option<Value>,
three_d_secure: Option<Value>,
network_transaction_id: Option<String>,
extended_authorization: Option<StripeExtendedAuthorizationResponse>,
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
pub capture_before: Option<PrimitiveDateTime>,
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct StripeExtendedAuthorizationResponse {
status: Option<StripeExtendedAuthorizationStatus>,
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum StripeExtendedAuthorizationStatus {
Disabled,
Enabled,
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
@ -2586,25 +2643,45 @@ pub enum StripePaymentMethodDetailsResponse {
pub struct AdditionalPaymentMethodDetails {
pub payment_checks: Option<Value>,
pub authentication_details: Option<Value>,
pub extended_authorization: Option<StripeExtendedAuthorizationResponse>,
pub capture_before: Option<PrimitiveDateTime>,
}
impl From<AdditionalPaymentMethodDetails> for AdditionalPaymentMethodConnectorResponse {
fn from(item: AdditionalPaymentMethodDetails) -> Self {
impl From<&AdditionalPaymentMethodDetails> for AdditionalPaymentMethodConnectorResponse {
fn from(item: &AdditionalPaymentMethodDetails) -> Self {
Self::Card {
authentication_data: item.authentication_details,
payment_checks: item.payment_checks,
authentication_data: item.authentication_details.clone(),
payment_checks: item.payment_checks.clone(),
card_network: None,
domestic_network: None,
}
}
}
impl From<&AdditionalPaymentMethodDetails> for ExtendedAuthorizationResponseData {
fn from(item: &AdditionalPaymentMethodDetails) -> Self {
Self {
extended_authentication_applied: item.extended_authorization.as_ref().map(
|extended_authorization| {
primitive_wrappers::ExtendedAuthorizationAppliedBool::from(matches!(
extended_authorization.status,
Some(StripeExtendedAuthorizationStatus::Enabled)
))
},
),
capture_before: item.capture_before,
}
}
}
impl StripePaymentMethodDetailsResponse {
pub fn get_additional_payment_method_data(&self) -> Option<AdditionalPaymentMethodDetails> {
match self {
Self::Card { card } => Some(AdditionalPaymentMethodDetails {
payment_checks: card.checks.clone(),
authentication_details: card.three_d_secure.clone(),
extended_authorization: card.extended_authorization.clone(),
capture_before: card.capture_before,
}),
Self::Ideal { .. }
| Self::Bancontact { .. }
@ -2694,16 +2771,31 @@ pub struct SetupIntentResponse {
fn extract_payment_method_connector_response_from_latest_charge(
stripe_charge_enum: &StripeChargeEnum,
) -> Option<ConnectorResponseData> {
if let StripeChargeEnum::ChargeObject(charge_object) = stripe_charge_enum {
charge_object
.payment_method_details
.as_ref()
.and_then(StripePaymentMethodDetailsResponse::get_additional_payment_method_data)
let additional_payment_method_details =
if let StripeChargeEnum::ChargeObject(charge_object) = stripe_charge_enum {
charge_object
.payment_method_details
.as_ref()
.and_then(StripePaymentMethodDetailsResponse::get_additional_payment_method_data)
} else {
None
};
let additional_payment_method_data = additional_payment_method_details
.as_ref()
.map(AdditionalPaymentMethodConnectorResponse::from);
let extended_authorization_data = additional_payment_method_details
.as_ref()
.map(ExtendedAuthorizationResponseData::from);
if additional_payment_method_data.is_some() || extended_authorization_data.is_some() {
Some(ConnectorResponseData::new(
additional_payment_method_data,
extended_authorization_data,
))
} else {
None
}
.map(AdditionalPaymentMethodConnectorResponse::from)
.map(ConnectorResponseData::with_additional_payment_method_data)
}
fn extract_payment_method_connector_response_from_latest_attempt(
@ -2717,6 +2809,7 @@ fn extract_payment_method_connector_response_from_latest_attempt(
} else {
None
}
.as_ref()
.map(AdditionalPaymentMethodConnectorResponse::from)
.map(ConnectorResponseData::with_additional_payment_method_data)
}
@ -4166,6 +4259,7 @@ impl
ccard,
payment_method_auth_type,
item.request.request_incremental_authorization,
None,
))?)
}
PaymentMethodData::PayLater(_) => Ok(Self::PayLater(StripePayLaterData {

View File

@ -474,6 +474,16 @@ impl ConnectorResponseData {
extended_authorization_response_data: None,
}
}
pub fn new(
additional_payment_method_data: Option<AdditionalPaymentMethodConnectorResponse>,
extended_authorization_response_data: Option<ExtendedAuthorizationResponseData>,
) -> Self {
Self {
additional_payment_method_data,
extended_authorization_response_data,
}
}
pub fn get_extended_authorization_response_data(
&self,
) -> Option<&ExtendedAuthorizationResponseData> {