Bug: [REFACTOR] Adyen's balance platform webhooks



Webhooks for balance platform are different for different payout methods -

Banks - https://docs.adyen.com/payouts/payout-service/pay-out-to-bank-accounts/payout-webhooks/

Cards - https://docs.adyen.com/payouts/payout-service/pay-out-to-cards/payout-webhooks/
diff --git a/crates/hyperswitch_connectors/src/connectors/adyenplatform.rs b/crates/hyperswitch_connectors/src/connectors/adyenplatform.rs
index 78c8c4c8067..2158fece4c9 100644
--- a/crates/hyperswitch_connectors/src/connectors/adyenplatform.rs
+++ b/crates/hyperswitch_connectors/src/connectors/adyenplatform.rs
@@ -402,6 +402,7 @@ impl IncomingWebhook for Adyenplatform {
                 webhook_body.webhook_type,
                 webhook_body.data.status,
                 webhook_body.data.tracking,
+                webhook_body.data.category.as_ref(),
             ))
         }
         #[cfg(not(feature = "payouts"))]
diff --git a/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs b/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
index 6c964aea20f..b04ca410d39 100644
--- a/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
+++ b/crates/hyperswitch_connectors/src/connectors/adyenplatform/transformers/payouts.rs
@@ -513,6 +513,7 @@ pub struct AdyenplatformIncomingWebhookData {
     pub status: AdyenplatformWebhookStatus,
     pub reference: String,
     pub tracking: Option<AdyenplatformInstantStatus>,
+    pub category: Option<AdyenPayoutMethod>,
 }
 
 #[derive(Debug, Serialize, Deserialize)]
@@ -551,6 +552,7 @@ pub fn get_adyen_webhook_event(
     event_type: AdyenplatformWebhookEventType,
     status: AdyenplatformWebhookStatus,
     instant_status: Option<AdyenplatformInstantStatus>,
+    category: Option<&AdyenPayoutMethod>,
 ) -> webhooks::IncomingWebhookEvent {
     match (event_type, status, instant_status) {
         (AdyenplatformWebhookEventType::PayoutCreated, _, _) => {
@@ -565,9 +567,25 @@ pub fn get_adyen_webhook_event(
             }
         }
         (AdyenplatformWebhookEventType::PayoutUpdated, status, _) => match status {
-            AdyenplatformWebhookStatus::Authorised
-            | AdyenplatformWebhookStatus::Booked
-            | AdyenplatformWebhookStatus::Received => webhooks::IncomingWebhookEvent::PayoutCreated,
+            AdyenplatformWebhookStatus::Authorised | AdyenplatformWebhookStatus::Received => {
+                webhooks::IncomingWebhookEvent::PayoutCreated
+            }
+            AdyenplatformWebhookStatus::Booked => {
+                match category {
+                    Some(AdyenPayoutMethod::Card) => {
+                        // For card payouts, "booked" is the final success state
+                        webhooks::IncomingWebhookEvent::PayoutSuccess
+                    }
+                    Some(AdyenPayoutMethod::Bank) => {
+                        // For bank payouts, "booked" is intermediate - wait for final confirmation
+                        webhooks::IncomingWebhookEvent::PayoutProcessing
+                    }
+                    None => {
+                        // Default to processing if category is unknown
+                        webhooks::IncomingWebhookEvent::PayoutProcessing
+                    }
+                }
+            }
             AdyenplatformWebhookStatus::Pending => webhooks::IncomingWebhookEvent::PayoutProcessing,
             AdyenplatformWebhookStatus::Failed => webhooks::IncomingWebhookEvent::PayoutFailure,
             AdyenplatformWebhookStatus::Returned => webhooks::IncomingWebhookEvent::PayoutReversed,

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [ ] New feature
- [ ] Enhancement
- [x] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
This PR updates the webhooks for Adyen's BalancePlatform for payouts.

Main changes -
For status `booked` from Adyen, map it to
`success` for cards
`processing` for banks - reason being status `credited` is received from Adyen for successful credits.

Check the relevant issue for more details.

### Additional Changes

- [ ] This PR modifies the API contract
- [ ] This PR modifies the database schema
- [ ] This PR modifies application configuration/environment variables

<!--
Provide links to the files with corresponding changes.

Following are the paths where you can find config files:
1. `config`
2. `crates/router/src/configs`
3. `loadtest/config`
-->


## Motivation and Context
Enhances payout webhooks for Adyen's BalancePlatform.

## How did you test it?
<details>
    <summary>1. Create a successful card payout</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payouts/create' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_3ItwbKI2nwx45BPimAEaYP7QwnwKuC5a5GsG0aaow5wWefb62tXAcldZUrntn0jd' \
        --data '{"amount":100,"currency":"EUR","profile_id":"pro_S5yvh1TfnZi2Gjv7uSUQ","customer_id":"cus_2bXx7dm7bRTOLOMlBAQi","connector":["adyenplatform"],"description":"Its my first payout request","payout_type":"card","payout_method_data":{"card":{"card_number":"4111111111111111","expiry_month":"03","expiry_year":"2030","card_holder_name":"John Doe"}},"billing":{"address":{"line1":"1467","line2":"Harrison Street","city":"San Fransico","country":"FR","first_name":"John","last_name":"Doe"}},"recurring":true,"metadata":{"ref":"123"},"confirm":true,"auto_fulfill":true}'

Response

    {"payout_id":"payout_5nv2qm3avmPmQTbvFBZg","merchant_id":"merchant_1755690193","merchant_order_reference_id":null,"amount":100,"currency":"EUR","connector":"adyenplatform","payout_type":"card","payout_method_data":{"card":{"card_issuer":null,"card_network":null,"card_type":null,"card_issuing_country":null,"bank_code":null,"last4":"1111","card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"2030","card_holder_name":"John Doe"}},"billing":{"address":{"city":"San Fransico","country":"FR","line1":"1467","line2":"Harrison Street","line3":null,"zip":null,"state":null,"first_name":"John","last_name":"Doe","origin_zip":null},"phone":null,"email":null},"auto_fulfill":true,"customer_id":"cus_2bXx7dm7bRTOLOMlBAQi","customer":{"id":"cus_2bXx7dm7bRTOLOMlBAQi","name":"John Nether","email":null,"phone":"6168205362","phone_country_code":"+1"},"client_secret":"payout_payout_5nv2qm3avmPmQTbvFBZg_secret_j2B52JTji1yRrEOtd6FI","return_url":null,"business_country":null,"business_label":null,"description":"Its my first payout request","entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"merchant_connector_id":"mca_NsHlFe2GsWvK9CdftrLt","status":"initiated","error_message":null,"error_code":null,"profile_id":"pro_S5yvh1TfnZi2Gjv7uSUQ","created":"2025-08-20T11:49:11.757Z","connector_transaction_id":"38EBIY681J48U2JS","priority":null,"payout_link":null,"email":null,"name":"John Nether","phone":"6168205362","phone_country_code":"+1","unified_code":null,"unified_message":null,"payout_method_id":"pm_ywYBGKd0jKSUhIFyNho9"}

Wait for incoming webhook

Check for outgoing webhooks

Initiated

<img width="1726" height="906" alt="Screenshot 2025-08-20 at 5 21 04 PM" src="https://github.com/user-attachments/assets/74cc3db1-2338-4a31-8045-b5c54e156cfe" />

Successful

<img width="1726" height="891" alt="Screenshot 2025-08-20 at 5 21 12 PM" src="https://github.com/user-attachments/assets/ae8fd898-a3f8-4f78-adcf-12d6ddb4c8f8" />

</details>

<details>
    <summary>2. Create a successful SEPA payout</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payouts/create' \
        --header 'Accept-Language: fr' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_3ItwbKI2nwx45BPimAEaYP7QwnwKuC5a5GsG0aaow5wWefb62tXAcldZUrntn0jd' \
        --data '{"amount":1,"currency":"EUR","customer_id":"cus_2bXx7dm7bRTOLOMlBAQi","description":"Its my first payout request","payout_type":"bank","priority":"regular","payout_method_data":{"bank":{"iban":"NL57INGB4654188101"}},"connector":["adyenplatform"],"billing":{"address":{"line1":"Raadhuisplein","line2":"92","city":"Hoogeveen","country":"NL","first_name":"John"}},"entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"confirm":true,"auto_fulfill":true,"profile_id":"pro_S5yvh1TfnZi2Gjv7uSUQ"}'

Response

    {"payout_id":"payout_FywtoqrSxKtwDDsbCvGa","merchant_id":"merchant_1755690193","merchant_order_reference_id":null,"amount":1,"currency":"EUR","connector":"adyenplatform","payout_type":"bank","payout_method_data":{"bank":{"iban":"NL57I********88101","bank_name":null,"bank_country_code":null,"bank_city":null,"bic":null}},"billing":{"address":{"city":"Hoogeveen","country":"NL","line1":"Raadhuisplein","line2":"92","line3":null,"zip":null,"state":null,"first_name":"John","last_name":null,"origin_zip":null},"phone":null,"email":null},"auto_fulfill":true,"customer_id":"cus_2bXx7dm7bRTOLOMlBAQi","customer":{"id":"cus_2bXx7dm7bRTOLOMlBAQi","name":"John Nether","email":null,"phone":"6168205362","phone_country_code":"+1"},"client_secret":"payout_payout_FywtoqrSxKtwDDsbCvGa_secret_fC2TqhM2tucPGuzvQcDr","return_url":null,"business_country":null,"business_label":null,"description":"Its my first payout request","entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"merchant_connector_id":"mca_NsHlFe2GsWvK9CdftrLt","status":"initiated","error_message":null,"error_code":null,"profile_id":"pro_S5yvh1TfnZi2Gjv7uSUQ","created":"2025-08-20T11:51:48.487Z","connector_transaction_id":"38EBH1681J56C4WZ","priority":"regular","payout_link":null,"email":null,"name":"John Nether","phone":"6168205362","phone_country_code":"+1","unified_code":null,"unified_message":null,"payout_method_id":"pm_Vc6t4FK6HmbM2TUXsaBs"}

Wait for incoming webhook

Check for outgoing webhooks

Initiated

<img width="1728" height="898" alt="Screenshot 2025-08-20 at 5 22 39 PM" src="https://github.com/user-attachments/assets/a435370c-7954-4d02-8dc3-09b7bd242349" />

Successful

<img width="1728" height="892" alt="Screenshot 2025-08-20 at 5 22 46 PM" src="https://github.com/user-attachments/assets/5b17c1e3-2990-4b9b-9b42-4685a7271a97" />

</details>

## Checklist
<!-- Put an `x` in the boxes that apply -->

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