mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 01:27:31 +08:00
feat(payment_methods): enable auto-retries for apple pay (#4721)
Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
@ -3170,6 +3170,28 @@ where
|
|||||||
{
|
{
|
||||||
routing_data.business_sub_label = choice.sub_label.clone();
|
routing_data.business_sub_label = choice.sub_label.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if payment_data.payment_attempt.payment_method_type
|
||||||
|
== Some(storage_enums::PaymentMethodType::ApplePay)
|
||||||
|
{
|
||||||
|
let retryable_connector_data = helpers::get_apple_pay_retryable_connectors(
|
||||||
|
state,
|
||||||
|
merchant_account,
|
||||||
|
payment_data,
|
||||||
|
key_store,
|
||||||
|
connector_data.clone(),
|
||||||
|
#[cfg(feature = "connector_choice_mca_id")]
|
||||||
|
choice.merchant_connector_id.clone().as_ref(),
|
||||||
|
#[cfg(not(feature = "connector_choice_mca_id"))]
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if let Some(connector_data_list) = retryable_connector_data {
|
||||||
|
return Ok(ConnectorCallType::Retryable(connector_data_list));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return Ok(ConnectorCallType::PreDetermined(connector_data));
|
return Ok(ConnectorCallType::PreDetermined(connector_data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -122,36 +122,6 @@ fn is_dynamic_fields_required(
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_applepay_metadata(
|
|
||||||
connector_metadata: Option<common_utils::pii::SecretSerdeValue>,
|
|
||||||
) -> RouterResult<payment_types::ApplepaySessionTokenMetadata> {
|
|
||||||
connector_metadata
|
|
||||||
.clone()
|
|
||||||
.parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>(
|
|
||||||
"ApplepayCombinedSessionTokenData",
|
|
||||||
)
|
|
||||||
.map(|combined_metadata| {
|
|
||||||
api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
|
|
||||||
combined_metadata.apple_pay_combined,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.or_else(|_| {
|
|
||||||
connector_metadata
|
|
||||||
.parse_value::<api_models::payments::ApplepaySessionTokenData>(
|
|
||||||
"ApplepaySessionTokenData",
|
|
||||||
)
|
|
||||||
.map(|old_metadata| {
|
|
||||||
api_models::payments::ApplepaySessionTokenMetadata::ApplePay(
|
|
||||||
old_metadata.apple_pay,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.change_context(errors::ApiErrorResponse::InvalidDataFormat {
|
|
||||||
field_name: "connector_metadata".to_string(),
|
|
||||||
expected_format: "applepay_metadata_format".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_apple_pay_session_request(
|
fn build_apple_pay_session_request(
|
||||||
state: &routes::AppState,
|
state: &routes::AppState,
|
||||||
request: payment_types::ApplepaySessionRequest,
|
request: payment_types::ApplepaySessionRequest,
|
||||||
@ -196,7 +166,8 @@ async fn create_applepay_session_token(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// Get the apple pay metadata
|
// Get the apple pay metadata
|
||||||
let apple_pay_metadata = get_applepay_metadata(router_data.connector_meta_data.clone())?;
|
let apple_pay_metadata =
|
||||||
|
helpers::get_applepay_metadata(router_data.connector_meta_data.clone())?;
|
||||||
|
|
||||||
// Get payment request data , apple pay session request and merchant keys
|
// Get payment request data , apple pay session request and merchant keys
|
||||||
let (
|
let (
|
||||||
@ -213,6 +184,8 @@ async fn create_applepay_session_token(
|
|||||||
payment_request_data,
|
payment_request_data,
|
||||||
session_token_data,
|
session_token_data,
|
||||||
} => {
|
} => {
|
||||||
|
logger::info!("Apple pay simplified flow");
|
||||||
|
|
||||||
let merchant_identifier = state
|
let merchant_identifier = state
|
||||||
.conf
|
.conf
|
||||||
.applepay_merchant_configs
|
.applepay_merchant_configs
|
||||||
@ -254,6 +227,8 @@ async fn create_applepay_session_token(
|
|||||||
payment_request_data,
|
payment_request_data,
|
||||||
session_token_data,
|
session_token_data,
|
||||||
} => {
|
} => {
|
||||||
|
logger::info!("Apple pay manual flow");
|
||||||
|
|
||||||
let apple_pay_session_request =
|
let apple_pay_session_request =
|
||||||
get_session_request_for_manual_apple_pay(session_token_data.clone());
|
get_session_request_for_manual_apple_pay(session_token_data.clone());
|
||||||
|
|
||||||
@ -269,6 +244,8 @@ async fn create_applepay_session_token(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
payment_types::ApplepaySessionTokenMetadata::ApplePay(apple_pay_metadata) => {
|
payment_types::ApplepaySessionTokenMetadata::ApplePay(apple_pay_metadata) => {
|
||||||
|
logger::info!("Apple pay manual flow");
|
||||||
|
|
||||||
let apple_pay_session_request = get_session_request_for_manual_apple_pay(
|
let apple_pay_session_request = get_session_request_for_manual_apple_pay(
|
||||||
apple_pay_metadata.session_token_data.clone(),
|
apple_pay_metadata.session_token_data.clone(),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -3888,6 +3888,122 @@ pub fn validate_customer_access(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_apple_pay_simplified_flow(
|
||||||
|
connector_metadata: Option<pii::SecretSerdeValue>,
|
||||||
|
) -> CustomResult<bool, errors::ApiErrorResponse> {
|
||||||
|
let apple_pay_metadata = get_applepay_metadata(connector_metadata)?;
|
||||||
|
|
||||||
|
Ok(match apple_pay_metadata {
|
||||||
|
api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
|
||||||
|
apple_pay_combined_metadata,
|
||||||
|
) => match apple_pay_combined_metadata {
|
||||||
|
api_models::payments::ApplePayCombinedMetadata::Simplified { .. } => true,
|
||||||
|
api_models::payments::ApplePayCombinedMetadata::Manual { .. } => false,
|
||||||
|
},
|
||||||
|
api_models::payments::ApplepaySessionTokenMetadata::ApplePay(_) => false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_applepay_metadata(
|
||||||
|
connector_metadata: Option<pii::SecretSerdeValue>,
|
||||||
|
) -> RouterResult<api_models::payments::ApplepaySessionTokenMetadata> {
|
||||||
|
connector_metadata
|
||||||
|
.clone()
|
||||||
|
.parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>(
|
||||||
|
"ApplepayCombinedSessionTokenData",
|
||||||
|
)
|
||||||
|
.map(|combined_metadata| {
|
||||||
|
api_models::payments::ApplepaySessionTokenMetadata::ApplePayCombined(
|
||||||
|
combined_metadata.apple_pay_combined,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.or_else(|_| {
|
||||||
|
connector_metadata
|
||||||
|
.parse_value::<api_models::payments::ApplepaySessionTokenData>(
|
||||||
|
"ApplepaySessionTokenData",
|
||||||
|
)
|
||||||
|
.map(|old_metadata| {
|
||||||
|
api_models::payments::ApplepaySessionTokenMetadata::ApplePay(
|
||||||
|
old_metadata.apple_pay,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.change_context(errors::ApiErrorResponse::InvalidDataFormat {
|
||||||
|
field_name: "connector_metadata".to_string(),
|
||||||
|
expected_format: "applepay_metadata_format".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_apple_pay_retryable_connectors<F>(
|
||||||
|
state: AppState,
|
||||||
|
merchant_account: &domain::MerchantAccount,
|
||||||
|
payment_data: &mut PaymentData<F>,
|
||||||
|
key_store: &domain::MerchantKeyStore,
|
||||||
|
decided_connector_data: api::ConnectorData,
|
||||||
|
merchant_connector_id: Option<&String>,
|
||||||
|
) -> CustomResult<Option<Vec<api::ConnectorData>>, errors::ApiErrorResponse>
|
||||||
|
where
|
||||||
|
F: Send + Clone,
|
||||||
|
{
|
||||||
|
let profile_id = &payment_data
|
||||||
|
.payment_intent
|
||||||
|
.profile_id
|
||||||
|
.clone()
|
||||||
|
.get_required_value("profile_id")
|
||||||
|
.change_context(errors::ApiErrorResponse::MissingRequiredField {
|
||||||
|
field_name: "profile_id",
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let merchant_connector_account = get_merchant_connector_account(
|
||||||
|
&state,
|
||||||
|
merchant_account.merchant_id.as_str(),
|
||||||
|
payment_data.creds_identifier.to_owned(),
|
||||||
|
key_store,
|
||||||
|
profile_id, // need to fix this
|
||||||
|
&decided_connector_data.connector_name.to_string(),
|
||||||
|
merchant_connector_id,
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.get_metadata();
|
||||||
|
|
||||||
|
let connector_data_list = if is_apple_pay_simplified_flow(merchant_connector_account)? {
|
||||||
|
let merchant_connector_account_list = state
|
||||||
|
.store
|
||||||
|
.find_merchant_connector_account_by_merchant_id_and_disabled_list(
|
||||||
|
merchant_account.merchant_id.as_str(),
|
||||||
|
true,
|
||||||
|
key_store,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.to_not_found_response(errors::ApiErrorResponse::InternalServerError)?;
|
||||||
|
|
||||||
|
let mut connector_data_list = vec![decided_connector_data.clone()];
|
||||||
|
|
||||||
|
for merchant_connector_account in merchant_connector_account_list {
|
||||||
|
if is_apple_pay_simplified_flow(merchant_connector_account.metadata)? {
|
||||||
|
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||||
|
&state.conf.connectors,
|
||||||
|
&merchant_connector_account.connector_name.to_string(),
|
||||||
|
api::GetToken::Connector,
|
||||||
|
Some(merchant_connector_account.merchant_connector_id),
|
||||||
|
)
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Invalid connector name received")?;
|
||||||
|
|
||||||
|
if !connector_data_list.iter().any(|connector_details| {
|
||||||
|
connector_details.merchant_connector_id == connector_data.merchant_connector_id
|
||||||
|
}) {
|
||||||
|
connector_data_list.push(connector_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(connector_data_list)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Ok(connector_data_list)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
||||||
pub struct ApplePayData {
|
pub struct ApplePayData {
|
||||||
version: masking::Secret<String>,
|
version: masking::Secret<String>,
|
||||||
@ -4040,6 +4156,8 @@ impl ApplePayData {
|
|||||||
&self,
|
&self,
|
||||||
symmetric_key: &[u8],
|
symmetric_key: &[u8],
|
||||||
) -> CustomResult<String, errors::ApplePayDecryptionError> {
|
) -> CustomResult<String, errors::ApplePayDecryptionError> {
|
||||||
|
logger::info!("Decrypt apple pay token");
|
||||||
|
|
||||||
let data = BASE64_ENGINE
|
let data = BASE64_ENGINE
|
||||||
.decode(self.data.peek().as_bytes())
|
.decode(self.data.peek().as_bytes())
|
||||||
.change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;
|
.change_context(errors::ApplePayDecryptionError::Base64DecodingFailed)?;
|
||||||
|
|||||||
Reference in New Issue
Block a user