Bug: [BUG] recurring payouts fail when address is needed



### Bug Description

Recurring payouts are failing when billing details are not supplied during payout creation.

### Expected Behavior

Billing details should be fetched from DB while making recurring payouts.

### Actual Behavior

Billing details are not stored / fetched in payment_methods while making recurring payouts.

### Steps To Reproduce

1. Create a payout using raw details
2. Retrieve the `payout_method_id`
3. Process a payout using `payout_method_id` without billing details

Should throw an error saying billing details are mandatory.

```json
{
    "error": {
        "type": "invalid_request",
        "message": "Missing required param: billing.address",
        "code": "IR_04"
    }
}
```

### Context For The Bug

_No response_

### Environment

/ -

### Have you spent some time checking if this bug has been raised before?

- [x] I checked and didn't find a similar issue

### Have you read the Contributing Guidelines?

- [x] I have read the [Contributing Guidelines](https://github.com/juspay/hyperswitch/blob/main/docs/CONTRIBUTING.md)

### Are you willing to submit a PR?

None
diff --git a/crates/router/src/core/payments/routing.rs b/crates/router/src/core/payments/routing.rs
index c7784118847..e4ce038b3bd 100644
--- a/crates/router/src/core/payments/routing.rs
+++ b/crates/router/src/core/payments/routing.rs
@@ -175,7 +175,8 @@ pub fn make_dsl_input_for_payouts(
         billing_country: payout_data
             .billing_address
             .as_ref()
-            .and_then(|bic| bic.country)
+            .and_then(|ba| ba.address.as_ref())
+            .and_then(|addr| addr.country)
             .map(api_enums::Country::from_alpha2),
         business_label: payout_data.payout_attempt.business_label.clone(),
         setup_future_usage: None,
diff --git a/crates/router/src/core/payouts.rs b/crates/router/src/core/payouts.rs
index 300fca52939..491957045c7 100644
--- a/crates/router/src/core/payouts.rs
+++ b/crates/router/src/core/payouts.rs
@@ -29,7 +29,7 @@ use diesel_models::{
 use error_stack::{report, ResultExt};
 #[cfg(feature = "olap")]
 use futures::future::join_all;
-use hyperswitch_domain_models::payment_methods::PaymentMethod;
+use hyperswitch_domain_models::{self as domain_models, payment_methods::PaymentMethod};
 use masking::{PeekInterface, Secret};
 #[cfg(feature = "payout_retry")]
 use retry::GsmValidation;
@@ -66,7 +66,7 @@ use crate::{
 // ********************************************** TYPES **********************************************
 #[derive(Clone)]
 pub struct PayoutData {
-    pub billing_address: Option<domain::Address>,
+    pub billing_address: Option<domain_models::address::Address>,
     pub business_profile: domain::Profile,
     pub customer_details: Option<domain::Customer>,
     pub merchant_connector_account: Option<payment_helpers::MerchantConnectorAccountType>,
@@ -812,7 +812,7 @@ pub async fn payouts_list_core(
                     })
                     .ok()
                     .as_ref()
-                    .map(hyperswitch_domain_models::address::Address::from)
+                    .map(domain_models::address::Address::from)
                     .map(payment_enums::Address::from)
                 });
 
@@ -2537,10 +2537,7 @@ pub async fn response_handler(
     let billing_address = payout_data.billing_address.to_owned();
     let customer_details = payout_data.customer_details.to_owned();
     let customer_id = payouts.customer_id;
-    let billing = billing_address
-        .as_ref()
-        .map(hyperswitch_domain_models::address::Address::from)
-        .map(From::from);
+    let billing = billing_address.map(From::from);
 
     let translated_unified_message = helpers::get_translated_unified_code_and_message(
         state,
@@ -2672,29 +2669,17 @@ pub async fn payout_create_db_entries(
         _ => None,
     };
 
-    // We have to do this because the function that is being used to create / get address is from payments
-    // which expects a payment_id
-    let payout_id_as_payment_id_type = id_type::PaymentId::try_from(std::borrow::Cow::Owned(
-        payout_id.get_string_repr().to_string(),
-    ))
-    .change_context(errors::ApiErrorResponse::InvalidRequestData {
-        message: "payout_id contains invalid data".to_string(),
-    })
-    .attach_printable("Error converting payout_id to PaymentId type")?;
-
-    // Get or create address
-    let billing_address = payment_helpers::create_or_find_address_for_payment_by_request(
+    // Get or create billing address
+    let (billing_address, address_id) = helpers::resolve_billing_address_for_payout(
         state,
         req.billing.as_ref(),
         None,
-        merchant_id,
+        payment_method.as_ref(),
+        merchant_context,
         customer_id.as_ref(),
-        merchant_context.get_merchant_key_store(),
-        &payout_id_as_payment_id_type,
-        merchant_context.get_merchant_account().storage_scheme,
+        payout_id,
     )
     .await?;
-    let address_id = billing_address.to_owned().map(|address| address.address_id);
 
     // Make payouts entry
     let currency = req.currency.to_owned().get_required_value("currency")?;
@@ -2931,7 +2916,8 @@ pub async fn make_payout_data(
         &payout_id_as_payment_id_type,
         merchant_context.get_merchant_account().storage_scheme,
     )
-    .await?;
+    .await?
+    .map(|addr| domain_models::address::Address::from(&addr));
 
     let payout_id = &payouts.payout_id;
 
diff --git a/crates/router/src/core/payouts/helpers.rs b/crates/router/src/core/payouts/helpers.rs
index 8f104451be5..c80a089e5d4 100644
--- a/crates/router/src/core/payouts/helpers.rs
+++ b/crates/router/src/core/payouts/helpers.rs
@@ -4,7 +4,7 @@ use common_utils::{
     crypto::Encryptable,
     encryption::Encryption,
     errors::CustomResult,
-    ext_traits::{AsyncExt, StringExt},
+    ext_traits::{AsyncExt, StringExt, ValueExt},
     fp_utils, id_type, payout_method_utils as payout_additional, pii, type_name,
     types::{
         keymanager::{Identifier, KeyManagerState},
@@ -605,6 +605,22 @@ pub async fn save_payout_data_to_locker(
             )
         };
 
+    let payment_method_billing_address = payout_data
+        .billing_address
+        .clone()
+        .async_map(|billing_addr| async {
+            cards::create_encrypted_data(
+                &key_manager_state,
+                merchant_context.get_merchant_key_store(),
+                billing_addr,
+            )
+            .await
+        })
+        .await
+        .transpose()
+        .change_context(errors::ApiErrorResponse::InternalServerError)
+        .attach_printable("Unable to encrypt billing address")?;
+
     // Insert new entry in payment_methods table
     if should_insert_in_pm_table {
         let payment_method_id = common_utils::generate_id(consts::ID_LENGTH, "pm");
@@ -625,7 +641,7 @@ pub async fn save_payout_data_to_locker(
                 connector_mandate_details,
                 None,
                 None,
-                None,
+                payment_method_billing_address,
                 None,
                 None,
                 None,
@@ -1268,37 +1284,19 @@ pub async fn update_payouts_and_payout_attempt(
         payout_data.payouts.customer_id.clone()
     };
 
-    // We have to do this because the function that is being used to create / get address is from payments
-    // which expects a payment_id
-    let payout_id_as_payment_id_type = id_type::PaymentId::try_from(std::borrow::Cow::Owned(
-        payout_id.get_string_repr().to_string(),
-    ))
-    .change_context(errors::ApiErrorResponse::InvalidRequestData {
-        message: "payout_id contains invalid data for PaymentId conversion".to_string(),
-    })
-    .attach_printable("Error converting payout_id to PaymentId type")?;
-
-    // Fetch address details from request and create new or else use existing address that was attached
-    let billing_address = payment_helpers::create_or_find_address_for_payment_by_request(
+    let (billing_address, address_id) = resolve_billing_address_for_payout(
         state,
         req.billing.as_ref(),
-        None,
-        merchant_context.get_merchant_account().get_id(),
+        payout_data.payouts.address_id.as_ref(),
+        payout_data.payment_method.as_ref(),
+        merchant_context,
         customer_id.as_ref(),
-        merchant_context.get_merchant_key_store(),
-        &payout_id_as_payment_id_type,
-        merchant_context.get_merchant_account().storage_scheme,
+        &payout_id,
     )
     .await?;
-    let address_id = if billing_address.is_some() {
-        payout_data.billing_address = billing_address;
-        payout_data
-            .billing_address
-            .as_ref()
-            .map(|address| address.address_id.clone())
-    } else {
-        payout_data.payouts.address_id.clone()
-    };
+
+    // Update payout state with resolved billing address
+    payout_data.billing_address = billing_address;
 
     // Update DB with new data
     let payouts = payout_data.payouts.to_owned();
@@ -1536,3 +1534,105 @@ pub async fn get_additional_payout_data(
         }
     }
 }
+
+pub async fn resolve_billing_address_for_payout(
+    state: &SessionState,
+    req_billing: Option<&api_models::payments::Address>,
+    existing_address_id: Option<&String>,
+    payment_method: Option<&hyperswitch_domain_models::payment_methods::PaymentMethod>,
+    merchant_context: &domain::MerchantContext,
+    customer_id: Option<&id_type::CustomerId>,
+    payout_id: &id_type::PayoutId,
+) -> RouterResult<(
+    Option<hyperswitch_domain_models::address::Address>,
+    Option<String>,
+)> {
+    let payout_id_as_payment_id = id_type::PaymentId::try_from(std::borrow::Cow::Owned(
+        payout_id.get_string_repr().to_string(),
+    ))
+    .change_context(errors::ApiErrorResponse::InvalidRequestData {
+        message: "payout_id contains invalid data for PaymentId conversion".to_string(),
+    })
+    .attach_printable("Error converting payout_id to PaymentId type")?;
+
+    match (req_billing, existing_address_id, payment_method) {
+        // Address in request
+        (Some(_), _, _) => {
+            let billing_address = payment_helpers::create_or_find_address_for_payment_by_request(
+                state,
+                req_billing,
+                None,
+                merchant_context.get_merchant_account().get_id(),
+                customer_id,
+                merchant_context.get_merchant_key_store(),
+                &payout_id_as_payment_id,
+                merchant_context.get_merchant_account().storage_scheme,
+            )
+            .await?;
+            let address_id = billing_address.as_ref().map(|a| a.address_id.clone());
+            let hyperswitch_address = billing_address
+                .map(|addr| hyperswitch_domain_models::address::Address::from(&addr));
+            Ok((hyperswitch_address, address_id))
+        }
+
+        // Existing address using address_id
+        (None, Some(address_id), _) => {
+            let billing_address = payment_helpers::create_or_find_address_for_payment_by_request(
+                state,
+                None,
+                Some(address_id),
+                merchant_context.get_merchant_account().get_id(),
+                customer_id,
+                merchant_context.get_merchant_key_store(),
+                &payout_id_as_payment_id,
+                merchant_context.get_merchant_account().storage_scheme,
+            )
+            .await?;
+            let hyperswitch_address = billing_address
+                .map(|addr| hyperswitch_domain_models::address::Address::from(&addr));
+            Ok((hyperswitch_address, Some(address_id.clone())))
+        }
+
+        // Existing address in stored payment method
+        (None, None, Some(pm)) => {
+            pm.payment_method_billing_address.as_ref().map_or_else(
+                || {
+                    logger::info!("No billing address found in payment method");
+                    Ok((None, None))
+                },
+                |encrypted_billing_address| {
+                    logger::info!("Found encrypted billing address data in payment method");
+
+                    #[cfg(feature = "v1")]
+                    {
+                        encrypted_billing_address
+                            .clone()
+                            .into_inner()
+                            .expose()
+                            .parse_value::<hyperswitch_domain_models::address::Address>(
+                                "payment_method_billing_address",
+                            )
+                            .map(|domain_address| {
+                                logger::info!("Successfully parsed as hyperswitch_domain_models::address::Address");
+                                (Some(domain_address), None)
+                            })
+                            .map_err(|e| {
+                                logger::error!("Failed to parse billing address into (hyperswitch_domain_models::address::Address): {:?}", e);
+                                errors::ApiErrorResponse::InternalServerError
+                            })
+                            .attach_printable("Failed to parse stored billing address")
+                    }
+
+                    #[cfg(feature = "v2")]
+                    {
+                        // TODO: Implement v2 logic when needed
+                        logger::warn!("v2 billing address resolution not yet implemented");
+                        Ok((None, None))
+                    }
+                },
+            )
+        }
+
+        (None, None, None) => Ok((None, None)),
+    }
+}
diff --git a/crates/router/src/core/utils.rs b/crates/router/src/core/utils.rs
index 4a56078858f..432a148fc41 100644
--- a/crates/router/src/core/utils.rs
+++ b/crates/router/src/core/utils.rs
@@ -98,30 +98,7 @@ pub async fn construct_payout_router_data<'a, F>(
 
     let billing = payout_data.billing_address.to_owned();
 
-    let billing_address = billing.map(|a| {
-        let phone_details = api_models::payments::PhoneDetails {
-            number: a.phone_number.clone().map(Encryptable::into_inner),
-            country_code: a.country_code.to_owned(),
-        };
-        let address_details = api_models::payments::AddressDetails {
-            city: a.city.to_owned(),
-            country: a.country.to_owned(),
-            line1: a.line1.clone().map(Encryptable::into_inner),
-            line2: a.line2.clone().map(Encryptable::into_inner),
-            line3: a.line3.clone().map(Encryptable::into_inner),
-            zip: a.zip.clone().map(Encryptable::into_inner),
-            first_name: a.first_name.clone().map(Encryptable::into_inner),
-            last_name: a.last_name.clone().map(Encryptable::into_inner),
-            state: a.state.map(Encryptable::into_inner),
-            origin_zip: a.origin_zip.map(Encryptable::into_inner),
-        };
-
-        api_models::payments::Address {
-            phone: Some(phone_details),
-            address: Some(address_details),
-            email: a.email.to_owned().map(Email::from),
-        }
-    });
+    let billing_address = billing.map(api_models::payments::Address::from);
 
     let address = PaymentAddress::new(None, billing_address.map(From::from), None, None);
 

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

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

## Description
This PR adds capability for payouts to use billing address in payment_methods.

1. During first payout (with recurring: true) - payment_method entry created will store the billing address
2. During recurring payouts - stored billing address will be fetched and used

### 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`
4. `crates/router/src/configs`
5. `loadtest/config`
-->


## Motivation and Context
This fixes the bug where it required address as a mandatory field for recurring payouts (payout using stored tokens).

## How did you test it?
Scenarios to test (for address)
1. Processing CIPT and using that token for processing payout
2. Processing a recurring payout and using that token for processing payout

<details>
    <summary>1. Create payments CIT</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payments' \
        --header 'Content-Type: application/json' \
        --header 'Accept: application/json' \
        --header 'api-key: dev_nCYTBLS606xu5drbUngHWbjGv7PNWKuXbYzppYkoykcCnz4vFyEqbdW1hKW1BBUy' \
        --data-raw '{"amount":4500,"currency":"EUR","confirm":true,"profile_id":"pro_XpJkLrIhn3boVv94ixmE","capture_method":"automatic","authentication_type":"three_ds","setup_future_usage":"off_session","customer_id":"cus_MQ79t5dcAGyQnVVXZ5vg","email":"abc@example.com","return_url":"https://google.com","payment_method":"card","payment_method_data":{"card":{"card_number":"4111111111111111","card_exp_month":"03","card_exp_year":"2030","card_cvc":"737"},"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","zip":"94122","country":"IT","first_name":"joseph","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"}},"metadata":{"udf1":"value1","new_customer":"true","login_date":"2019-09-10T10:11:12Z"},"browser_info":{"user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","language":"en-US","color_depth":32,"screen_height":1117,"screen_width":1728,"time_zone":-330,"java_enabled":true,"java_script_enabled":true,"ip_address":"127.0.0.1"},"customer_acceptance":{"acceptance_type":"online","accepted_at":"1963-05-03T04:07:52.723Z","online":{"ip_address":"127.0.0.1","user_agent":"amet irure esse"}},"session_expiry":2890}'

Response

    {"payment_id":"pay_DeekxhJrZ9CWyCTQCq4r","merchant_id":"merchant_1756974399","status":"succeeded","amount":4500,"net_amount":4500,"shipping_cost":null,"amount_capturable":0,"amount_received":4500,"connector":"adyen","client_secret":"pay_DeekxhJrZ9CWyCTQCq4r_secret_Ch5L75UesuEC9Err8YZU","created":"2025-09-04T09:36:22.282Z","currency":"EUR","customer_id":"cus_MQ79t5dcAGyQnVVXZ5vg","customer":{"id":"cus_MQ79t5dcAGyQnVVXZ5vg","name":"Albert Klaassen","email":"abc@example.com","phone":"6168205362","phone_country_code":"+1"},"description":null,"refunds":null,"disputes":null,"mandate_id":null,"mandate_data":null,"setup_future_usage":"off_session","off_session":null,"capture_on":null,"capture_method":"automatic","payment_method":"card","payment_method_data":{"card":{"last4":"1111","card_type":"CREDIT","card_network":"Visa","card_issuer":"JP Morgan","card_issuing_country":"INDIA","card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"2030","card_holder_name":"joseph Doe","payment_checks":null,"authentication_data":null},"billing":{"address":{"city":"San Fransico","country":"IT","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":null,"first_name":"joseph","last_name":"Doe","origin_zip":null},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"}},"payment_token":null,"shipping":null,"billing":null,"order_details":null,"email":"abc@example.com","name":"Albert Klaassen","phone":"6168205362","return_url":"https://google.com/","authentication_type":"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":"cus_MQ79t5dcAGyQnVVXZ5vg","created_at":1756978582,"expires":1756982182,"secret":"epk_27ed6fdabc164aceb5e0aa888832c373"},"manual_retry_allowed":false,"connector_transaction_id":"WDV92WCPCC5FX875","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_DeekxhJrZ9CWyCTQCq4r_1","payment_link":null,"profile_id":"pro_XpJkLrIhn3boVv94ixmE","surcharge_details":null,"attempt_count":1,"merchant_decision":null,"merchant_connector_id":"mca_Z5SC1uhu0kS6KvrnsxNY","incremental_authorization_allowed":false,"authorization_count":null,"incremental_authorizations":null,"external_authentication_details":null,"external_3ds_authentication_attempted":false,"expires_on":"2025-09-04T10:24:32.282Z","fingerprint":null,"browser_info":{"language":"en-US","time_zone":-330,"ip_address":"127.0.0.1","user_agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36","color_depth":32,"java_enabled":true,"screen_width":1728,"accept_header":"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8","screen_height":1117,"java_script_enabled":true},"payment_channel":null,"payment_method_id":"pm_zRVZxGagdzsBMg5LmYct","network_transaction_id":"900345051761757","payment_method_status":"active","updated":"2025-09-04T09:36:23.649Z","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":"GJ7C3STP34XQLV75","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}

</details>

<details>
    <summary>2. Process payout using stored payment_method_id</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payouts/create' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_nCYTBLS606xu5drbUngHWbjGv7PNWKuXbYzppYkoykcCnz4vFyEqbdW1hKW1BBUy' \
        --data '{"payout_method_id":"pm_zRVZxGagdzsBMg5LmYct","amount":1,"currency":"EUR","customer_id":"cus_MQ79t5dcAGyQnVVXZ5vg","phone_country_code":"+65","description":"Its my first payout request","connector":["adyen"],"entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"confirm":true,"auto_fulfill":true,"profile_id":"pro_XpJkLrIhn3boVv94ixmE"}'

Response

    {"payout_id":"payout_uPw0KZVYyb3RnOrChh9Q","merchant_id":"merchant_1756974399","merchant_order_reference_id":null,"amount":1,"currency":"EUR","connector":"adyen","payout_type":"card","payout_method_data":{"card":{"card_issuer":"JP Morgan","card_network":"Visa","card_type":"CREDIT","card_issuing_country":"INDIA","bank_code":"JP_JPM","last4":"1111","card_isin":"411111","card_extended_bin":null,"card_exp_month":"03","card_exp_year":"2030","card_holder_name":"joseph Doe"}},"billing":{"address":{"city":"San Fransico","country":"IT","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":null,"first_name":"joseph","last_name":"Doe","origin_zip":null},"phone":{"number":"8056594427","country_code":"+91"},"email":"guest@example.com"},"auto_fulfill":true,"customer_id":"cus_MQ79t5dcAGyQnVVXZ5vg","customer":{"id":"cus_MQ79t5dcAGyQnVVXZ5vg","name":"Albert Klaassen","email":"abc@example.com","phone":"6168205362","phone_country_code":"+1"},"client_secret":"payout_payout_uPw0KZVYyb3RnOrChh9Q_secret_lVBCc5ylHYJonPY9Zhb3","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_5EG2zvgrEvH6Lv8xm3Ni","status":"success","error_message":null,"error_code":null,"profile_id":"pro_XpJkLrIhn3boVv94ixmE","created":"2025-09-04T09:37:16.364Z","connector_transaction_id":"RWZXLHMQMCJXKN75","priority":null,"payout_link":null,"email":"abc@example.com","name":"Albert Klaassen","phone":"6168205362","phone_country_code":"+1","unified_code":null,"unified_message":null,"payout_method_id":"pm_zRVZxGagdzsBMg5LmYct"}

</details>

<details>
    <summary>3. Create payout using raw details</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payouts/create' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_Iikb8EKUsyOh2DtqlE3HmqD6xdzI47h94XHiKKHTgA5YVT1ng4iyMCqSCBgsUDf8' \
        --data-raw '{"amount":100,"currency":"EUR","profile_id":"pro_tQvMhyOhPPzlg5agzwAM","customer":{"id":"cus_Z51PFM8B1iROSJcMzllP","email":"new_cust@example.com","name":"John Doe","phone":"999999999","phone_country_code":"+65"},"connector":["adyen"],"description":"Its my first payout request","payout_type":"card","payout_method_data":{"card":{"card_number":"4111 1111 1111 1111","expiry_month":"03","expiry_year":"2030","card_holder_name":"John Doe"}},"billing":{"address":{"line1":"1467","line2":"Harrison Street","line3":"Harrison Street","city":"San Fransico","state":"CA","zip":"94122","country":"US","first_name":"John","last_name":"Doe"},"phone":{"number":"8056594427","country_code":"+91"}},"entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"confirm":true,"auto_fulfill":true}'

Response

   {"payout_id":"payout_p9MwtbQ0ta4tItSgXqBf","merchant_id":"merchant_1756971567","merchant_order_reference_id":null,"amount":100,"currency":"EUR","connector":"adyen","payout_type":"card","payout_method_data":{"card":{"card_issuer":"JP Morgan","card_network":"Visa","card_type":"CREDIT","card_issuing_country":"INDIA","bank_code":"JP_JPM","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":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe","origin_zip":null},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"auto_fulfill":true,"customer_id":"cus_Z51PFM8B1iROSJcMzllP","customer":{"id":"cus_Z51PFM8B1iROSJcMzllP","name":"Albert Klaassen","email":null,"phone":"6168205362","phone_country_code":"+1"},"client_secret":"payout_payout_p9MwtbQ0ta4tItSgXqBf_secret_HiUN8ag2hf8gWxZj3HuY","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_5fXDmw6DZLP3aod1Zhsn","status":"success","error_message":null,"error_code":null,"profile_id":"pro_tQvMhyOhPPzlg5agzwAM","created":"2025-09-04T07:41:23.654Z","connector_transaction_id":"GB85JNB87RXVS3V5","priority":null,"payout_link":null,"email":null,"name":"Albert Klaassen","phone":"6168205362","phone_country_code":"+1","unified_code":null,"unified_message":null,"payout_method_id":"pm_cNt6gdwVpysyWzbHIDmo"}

</details>

<details>
    <summary>4. Process payout using payout_method_id</summary>

cURL

    curl --location --request POST 'http://localhost:8080/payouts/create' \
        --header 'Content-Type: application/json' \
        --header 'api-key: dev_Iikb8EKUsyOh2DtqlE3HmqD6xdzI47h94XHiKKHTgA5YVT1ng4iyMCqSCBgsUDf8' \
        --data '{"payout_method_id":"pm_cNt6gdwVpysyWzbHIDmo","amount":1,"currency":"EUR","customer_id":"cus_Z51PFM8B1iROSJcMzllP","phone_country_code":"+65","description":"Its my first payout request","connector":["adyen"],"entity_type":"Individual","recurring":true,"metadata":{"ref":"123"},"confirm":true,"auto_fulfill":true,"profile_id":"pro_tQvMhyOhPPzlg5agzwAM"}'

Response

    {"payout_id":"payout_NKBml9En5YOW4ZfPKoDI","merchant_id":"merchant_1756971567","merchant_order_reference_id":null,"amount":1,"currency":"EUR","connector":"adyen","payout_type":"card","payout_method_data":{"card":{"card_issuer":"JP Morgan","card_network":"Visa","card_type":"CREDIT","card_issuing_country":"INDIA","bank_code":"JP_JPM","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":"US","line1":"1467","line2":"Harrison Street","line3":"Harrison Street","zip":"94122","state":"CA","first_name":"John","last_name":"Doe","origin_zip":null},"phone":{"number":"8056594427","country_code":"+91"},"email":null},"auto_fulfill":true,"customer_id":"cus_Z51PFM8B1iROSJcMzllP","customer":{"id":"cus_Z51PFM8B1iROSJcMzllP","name":"Albert Klaassen","email":null,"phone":"6168205362","phone_country_code":"+1"},"client_secret":"payout_payout_NKBml9En5YOW4ZfPKoDI_secret_fhKX0pr2SlIZjRDynimH","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_5fXDmw6DZLP3aod1Zhsn","status":"success","error_message":null,"error_code":null,"profile_id":"pro_tQvMhyOhPPzlg5agzwAM","created":"2025-09-04T07:44:05.254Z","connector_transaction_id":"LV4DQ6NDVV652VT5","priority":null,"payout_link":null,"email":null,"name":"Albert Klaassen","phone":"6168205362","phone_country_code":"+1","unified_code":null,"unified_message":null,"payout_method_id":"pm_cNt6gdwVpysyWzbHIDmo"}

</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`
- [ ] I reviewed the submitted code
- [ ] I added unit tests for my changes where possible
