Repository: juspay/hyperswitch
File: crates/router/src/core/payments.rs (Rust)

Code before:
                }
            }
        } else {
            None
        };

        let enable_predecrypted_token = merchant_connector_account
            .get_metadata()
            .parse_value::<api_models::payments::ApplepayCombinedSessionTokenData>(
                "ApplepayCombinedSessionTokenData",
            )
            .ok()
            .and_then(|apple_pay_metadata| {
                apple_pay_metadata
                    .apple_pay_combined
                    .enable_predecrypted_token
            })
            .unwrap_or(false);
        match (pre_decrypted_token, enable_predecrypted_token) {
            (Some(_token), false) => Err(errors::ApiErrorResponse::PreconditionFailed {
                message: "Predecrypted token is not enabled for ApplePay".to_string(),
            }
            .into()),
            (Some(token), true) => Ok(Some(token)),
            (None, _) => Ok(None),
        }
    }

    fn decide_wallet_flow(
        &self,
        state: &SessionState,
        payment_data: &D,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
    ) -> CustomResult<Option<DecideWalletFlow>, errors::ApiErrorResponse> {
        let apple_pay_metadata = check_apple_pay_metadata(state, Some(merchant_connector_account));

        add_apple_pay_flow_metrics(
            &apple_pay_metadata,
            payment_data.get_payment_attempt().connector.clone(),
            payment_data.get_payment_attempt().merchant_id.clone(),
        );

        let wallet_flow = match apple_pay_metadata {
            Some(domain::ApplePayFlow::Simplified(payment_processing_details)) => Some(
                DecideWalletFlow::ApplePayDecrypt(payment_processing_details),
            ),
            Some(domain::ApplePayFlow::Manual) | None => None,
        };
        Ok(wallet_flow)
    }


Review comment (suggestion):
We should validate the support_pre_decrypted_token is enabled only if the apple pay payment data is of the type decrypted token, basically in the `common_payments_types::ApplePayPaymentData::Decrypted`.

Same goes for other wallets.

Code after:
        Ok(router_data)
    }?;

    let etime_connector = Instant::now();
    let duration_connector = etime_connector.saturating_duration_since(stime_connector);
    tracing::info!(duration = format!("Duration taken: {}", duration_connector.as_millis()));

    Ok(router_data)
}
struct ApplePayWallet;
struct PazeWallet;
struct GooglePayWallet;

#[async_trait::async_trait]
pub trait WalletFlow<F, D>: Send + Sync
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    /// Check if wallet data is already decrypted and return token if so
    fn check_predecrypted_token(
        &self,
        _payment_data: &D,
        _merchant_connector_account: &helpers::MerchantConnectorAccountType,
    ) -> CustomResult<Option<PaymentMethodToken>, errors::ApiErrorResponse> {
        // Default implementation returns None (no pre-decrypted data)
        Ok(None)
    }

    fn decide_wallet_flow(
        &self,
        state: &SessionState,
        payment_data: &D,
        merchant_connector_account: &helpers::MerchantConnectorAccountType,
    ) -> CustomResult<Option<DecideWalletFlow>, errors::ApiErrorResponse>;

    async fn decrypt_wallet_token(
        &self,
        wallet_flow: &DecideWalletFlow,
        payment_data: &D,
    ) -> CustomResult<PaymentMethodToken, errors::ApiErrorResponse>;
}

#[async_trait::async_trait]
impl<F, D> WalletFlow<F, D> for PazeWallet
where
    F: Send + Clone,
    D: OperationSessionGetters<F> + Send + Sync + Clone,
{
    fn decide_wallet_flow(
        &self,