Repository: juspay/hyperswitch
File: crates/hyperswitch_connectors/src/connectors/adyen.rs (Rust)

Code before:
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let optional_network_txn_id =
            notif
                .additional_data
                .network_tx_reference
                .map(|network_txn_id| {
                    hyperswitch_domain_models::router_flow_types::ConnectorNetworkTxnId::new(
                        network_txn_id,
                    )
                });
        Ok(optional_network_txn_id)
    }

    fn get_additional_payment_method_data(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<
        Option<api_models::payment_methods::PaymentMethodUpdate>,
        errors::ConnectorError,
    > {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        let card_expiry_in_parts = notif
            .additional_data
            .expiry_date // expiry_date is of format: "M/YYYY"
            .map(|date| {
                date.expose()
                    .split('/')
                    .map(|s| s.to_string())
                    .collect::<Vec<String>>()
            })
            .ok_or(errors::ConnectorError::ParsingFailed)?;

        if card_expiry_in_parts.len() == 2 {
            // Parse month as integer and format back as 2 digits
            let month = card_expiry_in_parts
                .get(1)
                .ok_or(errors::ConnectorError::ParsingFailed)?
                .parse::<u32>()
                .change_context(errors::ConnectorError::ParsingFailed)?;
            let month_str = Secret::new(format!("{:02}", month));

            // Parse year as integer and take last 2 digits
            let year = card_expiry_in_parts
                .get(2)
                .ok_or(errors::ConnectorError::ParsingFailed)?
                .parse::<u32>()
                .change_context(errors::ConnectorError::ParsingFailed)?;
            let year_str = Secret::new(format!("{:02}", year % 100));
            Ok(Some(api_models::payment_methods::PaymentMethodUpdate {

Review comment (security):
I have seen below formats in the response

`MM/YYYY`
`MM\/YYYY`

Let's handle the backward slash too (which is probably there to escape the forward slash)

Code after:

        let raw_key = hex::decode(connector_webhook_secrets.secret)
            .change_context(errors::ConnectorError::WebhookVerificationSecretInvalid)?;

        let signing_key = hmac::Key::new(hmac::HMAC_SHA256, &raw_key);
        let signed_messaged = hmac::sign(&signing_key, &message);
        let payload_sign = consts::BASE64_ENGINE.encode(signed_messaged.as_ref());
        Ok(payload_sign.as_bytes().eq(&signature))
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let notif = get_webhook_object_from_body(request.body)
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        // for capture_event, original_reference field will have the authorized payment's PSP reference
        if adyen::is_capture_or_cancel_or_adjust_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::ConnectorTransactionId(
                    notif
                        .original_reference
                        .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                ),
            ));
        }
        if adyen::is_transaction_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(notif.merchant_reference),
            ));
        }
        if adyen::is_refund_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::RefundId(
                api_models::webhooks::RefundIdType::RefundId(notif.merchant_reference),
            ));
        }
        if adyen::is_chargeback_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::ConnectorTransactionId(
                    notif
                        .original_reference
                        .ok_or(errors::ConnectorError::WebhookReferenceIdNotFound)?,
                ),
            ));
        }
        #[cfg(feature = "payouts")]
        if adyen::is_payout_event(&notif.event_code) {
            return Ok(api_models::webhooks::ObjectReferenceId::PayoutId(
                api_models::webhooks::PayoutIdType::PayoutAttemptId(notif.merchant_reference),
            ));
        }