Skip to content

feat(router): Handle incoming webhooks adyen#9372

Merged
likhinbopanna merged 1 commit intomainfrom
handle-incoming-webhooks-adyen
Sep 17, 2025
Merged

feat(router): Handle incoming webhooks adyen#9372
likhinbopanna merged 1 commit intomainfrom
handle-incoming-webhooks-adyen

Conversation

@mrudulvajpayee4935
Copy link
Contributor

@mrudulvajpayee4935 mrudulvajpayee4935 commented Sep 15, 2025

Type of Change

  • Bugfix
  • New feature
  • Enhancement
  • Refactoring
  • Dependency updates
  • Documentation
  • CI/CD

Description

Add acknowledgement in adyen for incoming webhooks by sending status 200.

Additional Changes

  • This PR modifies the API contract
  • This PR modifies the database schema
  • This PR modifies application configuration/environment variables

Motivation and Context

How did you test it?

We need to create two profiles having same adyen account, both the profiles should return 200 to webhooks even if it doesnt belongs to them.
here i made two profiles:

pro_JcW9GqodbWptCntfDiiJ : mca_kAzlkhIkGnErSSc3L6Xr
pro_4vFskA1mTL8nf81s9Fzq : mca_4F3FOjPSWuvclkKoiG06

Making below payments API:

curl --location 'http://localhost:8080/payments' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_**' \
--data-raw '{
    "amount": 700,
    "currency": "USD",
    "confirm": true,
    "customer_id":"StripeCustomer",
    "payment_link": false,
    "capture_on": "2029-09-10T10:11:12Z",
    "amount_to_capture": 700,
    "name": "John Doe",
    "email": "dg@example.com",
    "phone": "999999999",
    "phone_country_code": "+1",
    "description": "Its my first payment request",
    "authentication_type": "no_three_ds",
    "return_url": "https://google.com",
    "payment_method_data": {
        "card": {
            "card_number": "4111111111111111",
            "card_exp_month": "03",
            "card_exp_year": "30",
            "card_cvc": "737"
        },
        "billing": {
            "address": {
                "line1": "1467",
                "line2": "CA",
                "city": "San Fransico",
                "state": "California",
                "zip": "94122",
                "country": "US"
            }
        }
    },
    "payment_method": "card",
    "payment_method_type": "credit",
    "profile_id": "pro_JcW9GqodbWptCntfDiiJ",
    "browser_info": {
        "color_depth": 24,
        "java_enabled": true,
        "java_script_enabled": true,
        "language": "en-GB",
        "screen_height": 720,
        "screen_width": 1280,
        "time_zone": -330,
        "ip_address": "208.127.127.193",
        "accept_header": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0"
    },
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    },
    "order_details": [
        {
            "product_name": "Tea",
            "quantity": 1,
            "amount": 110,
            "product_img_link": "https://thumbs.dreamstime.com/b/indian-tea-spices-masala-chai-33827904.jpg"
        },
        {
            "product_name": "Tea",
            "quantity": 1,
            "amount": 110,
            "product_img_link": "https://thumbs.dreamstime.com/b/indian-tea-spices-masala-chai-33827904.jpg"
        },
        {
            "product_name": "Tea",
            "quantity": 1,
            "amount": 110,
            "product_img_link": "https://thumbs.dreamstime.com/b/indian-tea-spices-masala-chai-33827904.jpg"
        }
    ]
}'

Response:

{"payment_id":"pay_MGlcQq5UaVDvYSXAY6Xn","merchant_id":"merchant_1757925069","status":"succeeded","amount":700,"net_amount":700,"shipping_cost":null,"amount_capturable":0,"amount_received":700,"connector":"adyen","client_secret":"pay_MGlcQq5UaVDvYSXAY6Xn_secret_80ZstwkCzm9TyJVSzWLP","created":"2025-09-15T17:15:06.446Z","currency":"USD","customer_id":"StripeCustomer","customer":{"id":"StripeCustomer","name":"John Doe","email":"dg@example.com","phone":"999999999","phone_country_code":"+1"},"description":"Its my first payment request","refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":null,"off_session":null,"capture_on":null,"capture_method":null,"payment_method":"card","payment_method_data":{"card":{"last4":"1111","card_type":null,"card_network":null,"card_issuer":null,"card_issuing_country":null,"card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"30","card_holder_name":null,"payment_checks":null,"authentication_data":null},"billing":{"address":{"city":"San Fransico","country":"US","line1":"1467","line2":"CA","line3":null,"zip":"94122","state":"California","first_name":null,"last_name":null,"origin_zip":null},"phone":null,"email":null}},"payment_token":null,"shipping":null,"billing":null,"order_details":[{"sku":null,"upc":null,"brand":null,"amount":110,"category":null,"quantity":1,"tax_rate":null,"product_id":null,"description":null,"product_name":"Tea","product_type":null,"sub_category":null,"total_amount":null,"commodity_code":null,"unit_of_measure":null,"product_img_link":"https://thumbs.dreamstime.com/b/indian-tea-spices-masala-chai-33827904.jpg","product_tax_code":null,"total_tax_amount":null,"requires_shipping":null,"unit_discount_amount":null},{"sku":null,"upc":null,"brand":null,"amount":110,"category":null,"quantity":1,"tax_rate":null,"product_id":null,"description":null,"product_name":"Tea","product_type":null,"sub_category":null,"total_amount":null,"commodity_code":null,"unit_of_measure":null,"product_img_link":"https://thumbs.dreamstime.com/b/indian-tea-spices-masala-chai-33827904.jpg","product_tax_code":null,"total_tax_amount":null,"requires_shipping":null,"unit_discount_amount":null},{"sku":null,"upc":null,"brand":null,"amount":110,"category":null,"quantity":1,"tax_rate":null,"product_id":null,"description":null,"product_name":"Tea","product_type":null,"sub_category":null,"total_amount":null,"commodity_code":null,"unit_of_measure":null,"product_img_link":"https://thumbs.dreamstime.com/b/indian-tea-spices-masala-chai-33827904.jpg","product_tax_code":null,"total_tax_amount":null,"requires_shipping":null,"unit_discount_amount":null}],"email":"dg@example.com","name":"John Doe","phone":"999999999","return_url":"https://google.com/","authentication_type":"no_three_ds","statement_descriptor_name":null,"statement_descriptor_suffix":null,"next_action":null,"cancellation_reason":null,"error_code":null,"error_message":null,"unified_code":null,"unified_message":null,"payment_experience":null,"payment_method_type":"credit","connector_label":null,"business_country":null,"business_label":"default","business_sub_label":null,"allowed_payment_method_types":null,"ephemeral_key":{"customer_id":"StripeCustomer","created_at":1757956506,"expires":1757960106,"secret":"epk_"},"manual_retry_allowed":false,"connector_transaction_id":"MW2KB77642TBJWT5","frm_message":null,"metadata":{"udf1":"value1","login_date":"2019-09-10T10:11:12Z","new_customer":"true"},"connector_metadata":null,"feature_metadata":{"redirect_response":null,"search_tags":null,"apple_pay_recurring_details":null,"gateway_system":"direct"},"reference_id":"pay_MGlcQq5UaVDvYSXAY6Xn_1","payment_link":null,"profile_id":"pro_JcW9GqodbWptCntfDiiJ","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_kAzlkhIkGnErSSc3L6Xr","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-09-15T17:30:06.446Z","fingerprint":null,"browser_info":{"language":"en-GB","time_zone":-330,"ip_address":"208.127.127.193","user_agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0","color_depth":24,"java_enabled":true,"screen_width":1280,"accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","screen_height":720,"java_script_enabled":true},"payment_channel":null,"payment_method_id":null,"network_transaction_id":"596288259500941","payment_method_status":null,"updated":"2025-09-15T17:15:07.684Z","split_payments":null,"frm_metadata":null,"extended_authorization_applied":null,"capture_before":null,"merchant_order_reference_id":null,"order_tax_amount":null,"connector_mandate_id":null,"card_discovery":"manual","force_3ds_challenge":false,"force_3ds_challenge_trigger":false,"issuer_error_code":null,"issuer_error_message":null,"is_iframe_redirection_enabled":null,"whole_connector_response":null,"enable_partial_authorization":null,"enable_overcapture":null,"is_overcapture_enabled":null,"network_details":null}

Refund:

curl --location 'http://localhost:8080/refunds' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_OvYaTu43QFXUxM2Xs1KHS7YkWRkkh5nXp7x2BENotNMBzwCWVA0Yeug0f12ofDsB' \
--data '{
    "payment_id": "pay_MGlcQq5UaVDvYSXAY6Xn",
    "amount": 700,
    "reason": "Customer returned product",
    "refund_type": "instant",
    "metadata": {
        "udf1": "value1",
        "new_customer": "true",
        "login_date": "2019-09-10T10:11:12Z"
    }
}'

Response:

{"refund_id":"ref_aZfW2HjCEJKhRgsZguPr","payment_id":"pay_MGlcQq5UaVDvYSXAY6Xn","amount":700,"currency":"USD","status":"pending","reason":"Customer returned product","metadata":{"udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"},"error_message":null,"error_code":null,"unified_code":null,"unified_message":null,"created_at":"2025-09-15T17:16:30.637Z","updated_at":"2025-09-15T17:16:31.861Z","connector":"adyen","profile_id":"pro_JcW9GqodbWptCntfDiiJ","merchant_connector_id":"mca_kAzlkhIkGnErSSc3L6Xr","split_refunds":null,"issuer_error_code":null,"issuer_error_message":null}
image image (1) image (2) image image (6)

Checklist

  • I formatted the code cargo +nightly fmt --all
  • I addressed lints thrown by cargo clippy
  • I reviewed the submitted code
  • I added unit tests for my changes where possible

@mrudulvajpayee4935 mrudulvajpayee4935 requested review from a team as code owners September 15, 2025 04:32
@semanticdiff-com
Copy link

semanticdiff-com bot commented Sep 15, 2025

Review changes with  SemanticDiff

Changed Files
File Status
  crates/common_enums/src/connector_enums.rs  8% smaller
  crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs  0% smaller

@mrudulvajpayee4935 mrudulvajpayee4935 self-assigned this Sep 15, 2025
@mrudulvajpayee4935 mrudulvajpayee4935 added the A-webhooks Area: Webhook flows label Sep 15, 2025
@mrudulvajpayee4935 mrudulvajpayee4935 linked an issue Sep 15, 2025 that may be closed by this pull request
Copy link
Contributor

@kashif-m kashif-m left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit - remove comments

Comment on lines +898 to +901
let payment_intent = match payment_intent {
Ok(intent) => intent,
Err(_) => return Ok(None), // Payment resource doesn't exist -> NoEffect
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit : you can use ok_or to convert Result to Option

Copy link
Contributor Author

@mrudulvajpayee4935 mrudulvajpayee4935 Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We cannot use ok_or here as it is already in result format.


Ok(Some(mca))
}
webhooks::ObjectReferenceId::MandateId(mandate_id_type) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to handle this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sai-harsha-vardhan can you review this one?


Ok(Some(mca))
}
webhooks::ObjectReferenceId::ExternalAuthenticationID(auth_id_type) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, do we need to handle this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sai-harsha-vardhan this one as well

Comment on lines +722 to +727
pub async fn is_webhook_valid(
state: &SessionState,
object_reference_id: webhooks::ObjectReferenceId,
merchant_context: &domain::MerchantContext,
connector_name: &str,
) -> CustomResult<bool, errors::ApiErrorResponse> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this being called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removing this

connectors_with_webhook_source_verification_call = "paypal" # List of connectors which has additional source verification api-call

[webhook_ack_connectors]
connectors_with_webhook_ack = "adyen" # List of connectors which requires the acknowledgement for received webhook
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add adyenplatform

@mrudulvajpayee4935 mrudulvajpayee4935 force-pushed the handle-incoming-webhooks-adyen branch 2 times, most recently from 8f1a6bc to 7045dfe Compare September 15, 2025 08:04
kashif-m
kashif-m previously approved these changes Sep 16, 2025
@hrithikesh026
Copy link
Contributor

Have a look at this function.

fn is_webhook_source_verification_mandatory(&self) -> bool {

[webhook_source_verification_call]
connectors_with_webhook_source_verification_call = "paypal" # List of connectors which has additional source verification api-call

[webhook_ack_connectors]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be required, we already have fn should_acknowledge_webhook_for_resource_not_found_errors on Connector enum

}

#[cfg(feature = "v1")]
pub async fn get_authentication_from_authentication_id_type(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to duplicate all these functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is required because based on if we found the authentication details we are acknowledging the connector for webhook. i can use this inside existing find_mca_from_authentication_id_type so that no rewriting is required.

match result {
Ok((_, webhook_tracker, _)) => return Ok(webhook_tracker),
Err(e) => return Err(e),
match Box::pin(helper_utils::get_mca_for_webhook_ack_connectors(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need different getter for the same flow? Can't we use same function and handle the 404s?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could modify the existing function to check for resource's profile_id and send back None in case it does not belong?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might need to change the existing flow of the function get_mca_from_object_reference_id.
It currently only fetch mca no validation logics like in get_mca_for_webhook_ack_connectors.

@mrudulvajpayee4935 mrudulvajpayee4935 force-pushed the handle-incoming-webhooks-adyen branch from 01ea211 to d5e3d07 Compare September 17, 2025 15:33
@likhinbopanna likhinbopanna added this pull request to the merge queue Sep 17, 2025
Merged via the queue into main with commit 0e30fb6 Sep 17, 2025
25 of 29 checks passed
@likhinbopanna likhinbopanna deleted the handle-incoming-webhooks-adyen branch September 17, 2025 18:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-webhooks Area: Webhook flows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add acknowledgement in adyen for incoming webhooks

6 participants