Issue:
feat(core): Add record attempt operation for revenue recovery webhooks

## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->
**Revenue record operation** 
In revenue recovery webhook flow,  we mainly consume 3 types of events
1) PaymentFailure
2) PaymentSuccess
3) InvoiceCancel

For PaymentFailure or PaymentSuccess scenario we need to record the attempt and update the payment intent fields. This PR adds support for this operations.

**Why do we need to record the payment attempts ?**
To predict the next schedule time of mit transaction we need previous attempts information like error message, code and billing info, mandate details etc. So we store this info in payment_attempt against intent(invoice).

**Why do we need to update payment intent ?** 
Intent Status should be altered based on attempt status. Apart from this info like no of retries, payment method token details needs to be update in update trackers of record attempt operation.


### Additional Changes

- [x] 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
<!--
Why is this change required? What problem does it solve?
If it fixes an open issue, please link to the issue here.

If you don't have an issue, we'd recommend starting with one first so the PR
can focus on the implementation (unless it is an obvious bug or documentation fix
that will have little conversation).
-->


## How did you test it?
<!--
Did you write an integration/unit/API test to verify the code changes?
Or did you test this change manually (provide relevant screenshots)?
-->
Testcase 1: 
create payment connector mca : 
```
curl --location 'http://localhost:8080/v2/connector-accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-merchant-id: cloth_seller_FFzEuYGiJhp3SfL7AXoZ' \
--header 'x-profile-id: pro_gj36mgkfbJuFw2ZVvzX8' \
--header 'api-key: test_admin' \
--data '{
  "connector_type": "payment_processor",
  "connector_name": "stripe",
  "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "stripe apikey"
    },
  "connector_webhook_details": {
    "merchant_secret": ""
  },
  "profile_id": "pro_gj36mgkfbJuFw2ZVvzX8"
}'
```

response : 
```
{
    "connector_type": "payment_processor",
    "connector_name": "stripe",
    "connector_label": "stripe_business",
    "id": "mca_oAXqVf3BZs7bOS0iNhV7",
    "profile_id": "pro_gj36mgkfbJuFw2ZVvzX8",
    "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "sk*******************************************************************************************************5b"
    },
    "payment_methods_enabled": null,
    "connector_webhook_details": {
        "merchant_secret": "",
        "additional_secret": null
    },
    "metadata": null,
    "disabled": false,
    "frm_configs": null,
    "applepay_verified_domains": null,
    "pm_auth_config": null,
    "status": "active",
    "additional_merchant_data": null,
    "connector_wallets_details": null,
    "feature_metadata": null
}
```
get the payment connector mca and create billing connector mca 

Create billing connector mca 

```
curl --location 'http://localhost:8080/v2/connector-accounts' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'x-merchant-id: cloth_seller_FFzEuYGiJhp3SfL7AXoZ' \
--header 'x-profile-id: pro_gj36mgkfbJuFw2ZVvzX8' \
--header 'api-key: test_admin' \
--data '{
    "connector_type": "billing_processor",
    "connector_name": "chargebee",
    "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "chargebee apikey"
    },
    "connector_webhook_details": {
        "merchant_secret": "chargebee",
        "additional_secret" : "password"
    },
       "feature_metadata" : {
        "revenue_recovery" : {
            "max_retry_count" : 27,
            "billing_connector_retry_threshold" : 16,
            "billing_account_reference" :{
                "mca_oAXqVf3BZs7bOS0iNhV7" : "gw_16BlyOUaPA0eq12MZ"
            }
        }
    },
    "profile_id": "pro_gj36mgkfbJuFw2ZVvzX8"
}'
```
response : 
```
{
    "connector_type": "payment_processor",
    "connector_name": "chargebee",
    "connector_label": "chargebee_business",
    "id": "mca_1WvloGjsk5KcxOZ6fBPd",
    "profile_id": "pro_gj36mgkfbJuFw2ZVvzX8",
    "connector_account_details": {
        "auth_type": "HeaderKey",
        "api_key": "sk*******************************************************************************************************5b"
    },
    "payment_methods_enabled": null,
    "connector_webhook_details": {
        "merchant_secret": "chargebee",
        "additional_secret": "password"
    },
    "metadata": null,
    "disabled": false,
    "frm_configs": null,
    "applepay_verified_domains": null,
    "pm_auth_config": null,
    "status": "active",
    "additional_merchant_data": null,
    "connector_wallets_details": null,
    "feature_metadata": {
        "revenue_recovery": {
            "max_retry_count": 27,
            "billing_connector_retry_threshold": 16,
            "billing_account_reference": {
                "mca_oAXqVf3BZs7bOS0iNhV7": "gw_16BlyOUaPA0eq12MZ"
            }
        }
    }
}
```
Send chargebee simulated webhook either from dashboard or postman

request : 
```
curl --location 'http://localhost:8080/v2/webhooks/cloth_seller_FFzEuYGiJhp3SfL7AXoZ/pro_gj36mgkfbJuFw2ZVvzX8/mca_1WvloGjsk5KcxOZ6fBPd' \
--header 'Content-Type: application/json' \
--header 'Authorization: ••••••' \
--data-raw '{
  "id": "ev_169vy3UaPFn4L4mf",
  "occurred_at": 1737361021,
  "source": "admin_console",
  "user": "ns22eem1r18@student.nitw.ac.in",
  "object": "event",
  "api_version": "v2",
  "content": {
    "transaction": {
      "id": "txn_169vy3UaPFmC44me",
      "customer_id": "Azq8o5UaLqWWvyGd",
      "subscription_id": "Azq8o5UaLr2WnyHG",
      "gateway_account_id": "gw_16BlyOUaPA0eq12MZ",
      "payment_source_id": "pm_169vy3UaPDo0t4hL",
      "payment_method": "card",
      "gateway": "stripe",
      "type": "payment",
      "date": 1737361019,
      "exchange_rate": 1,
      "amount": 5,
      "id_at_gateway": "ch_3QjGAJSHworDX2hs0B120C0A",
      "status": "failure",
      "updated_at": 1737361021,
      "fraud_reason": "Payment complete.",
      "resource_version": 1737361021397,
      "deleted": false,
      "object": "transaction",
      "masked_card_number": "************4242",
      "currency_code": "INR",
      "base_currency_code": "INR",
      "amount_unused": 0,
      "linked_invoices": [
        {
          "invoice_id": "9",
          "applied_amount": 500000,
          "applied_at": 1737361021,
          "invoice_date": 1737361018,
          "invoice_total": 500000,
          "invoice_status": "paid"
        }
      ],
      "linked_refunds": [],
      "initiator_type": "merchant",
      "three_d_secure": false,
      "payment_method_details": "{\"card\":{\"first_name\":\"test2\",\"last_name\":\"name2\",\"iin\":\"424242\",\"last4\":\"4242\",\"funding_type\":\"credit\",\"expiry_month\":12,\"expiry_year\":2026,\"billing_addr1\":\"asdf\",\"billing_addr2\":\"asd\",\"billing_city\":\"asdf\",\"billing_state\":\"asdfaf\",\"billing_country\":\"AF\",\"billing_zip\":\"12345\",\"masked_number\":\"************4242\",\"object\":\"card\",\"brand\":\"visa\"}}"
    },
    "invoice": {
      "id": "invoice_1234",
      "customer_id": "Azq8o5UaLqWWvyGd",
      "subscription_id": "Azq8o5UaLr2WnyHG",
      "recurring": false,
      "status": "paid",
      "price_type": "tax_exclusive",
      "date": 1737361018,
      "due_date": 1737361018,
      "net_term_days": 0,
      "exchange_rate": 1,
      "total": 5,
      "amount_paid": 0,
      "amount_adjusted": 0,
      "write_off_amount": 0,
      "credits_applied": 0,
      "amount_due": 0,
      "paid_at": 1737361019,
      "updated_at": 1737361021,
      "resource_version": 1737361021401,
      "deleted": false,
      "object": "invoice",
      "first_invoice": false,
      "amount_to_collect": 0,
      "round_off_amount": 0,
      "has_advance_charges": false,
      "currency_code": "INR",
      "base_currency_code": "INR",
      "generated_at": 1737361018,
      "is_gifted": false,
      "term_finalized": true,
      "channel": "web",
      "tax": 0,
      "line_items": [
        {
          "id": "li_169vy3UaPFmBR4md",
          "date_from": 1737361004,
          "date_to": 1737361004,
          "unit_amount": 500000,
          "quantity": 1,
          "amount": 500000,
          "pricing_model": "flat_fee",
          "is_taxed": false,
          "tax_amount": 0,
          "object": "line_item",
          "subscription_id": "Azq8o5UaLr2WnyHG",
          "customer_id": "Azq8o5UaLqWWvyGd",
          "description": "Implementation Charge",
          "entity_type": "charge_item_price",
          "entity_id": "cbdemo_implementation-charge-INR",
          "tax_exempt_reason": "tax_not_configured",
          "discount_amount": 0,
          "item_level_discount_amount": 0
        }
      ],
      "sub_total": 500000,
      "linked_payments": [
        {
          "txn_id": "txn_169vy3UaPFmC44me",
          "applied_amount": 500000,
          "applied_at": 1737361021,
          "txn_status": "success",
          "txn_date": 1737361019,
          "txn_amount": 500000
        }
      ],
      "applied_credits": [],
      "adjustment_credit_notes": [],
      "issued_credit_notes": [],
      "linked_orders": [],
      "dunning_attempts": [],
      "billing_address": {
        "first_name": "test1",
        "last_name": "name",
        "email": "johndoe@gmail.com",
        "company": "johndoe",
        "phone": "+91 83 17 575848",
        "line1": "asdf",
        "line2": "asd",
        "line3": "ahjkd",
        "city": "asdf",
        "state_code": "TG",
        "state": "Telangana",
        "country": "IN",
        "zip": "561432",
        "validation_status": "not_validated",
        "object": "billing_address"
      },
      "site_details_at_creation": {
        "timezone": "Asia/Calcutta"
      }
    },
    "customer": {
      "id": "Azq8o5UaLqWWvyGd",
      "first_name": "john",
      "last_name": "doe",
      "email": "john@gmail.com",
      "phone": "831 757 5848",
      "company": "johndoe",
      "auto_collection": "on",
      "net_term_days": 0,
      "allow_direct_debit": false,
      "created_at": 1737310670,
      "created_from_ip": "205.254.163.189",
      "taxability": "taxable",
      "updated_at": 1737360899,
      "pii_cleared": "active",
      "channel": "web",
      "resource_version": 1737360899990,
      "deleted": false,
      "object": "customer",
      "billing_address": {
        "first_name": "test1",
        "last_name": "name",
        "email": "johndoe@gmail.com",
        "company": "johndoe",
        "phone": "+91 83 17 575848",
        "line1": "asdf",
        "line2": "asd",
        "line3": "ahjkd",
        "city": "asdf",
        "state_code": "TG",
        "state": "Telangana",
        "country": "IN",
        "zip": "561432",
        "validation_status": "not_validated",
        "object": "billing_address"
      },
      "card_status": "valid",
      "promotional_credits": 0,
      "refundable_credits": 0,
      "excess_payments": 0,
      "unbilled_charges": 0,
      "preferred_currency_code": "INR",
      "mrr": 0,
      "primary_payment_source_id": "pm_169vy3UaPDo0t4hL",
      "payment_method": {
        "object": "payment_method",
        "type": "card",
        "reference_id": "cus_RcUo8xTwe0sHP7/card_1QjG2dSHworDX2hs6YIjKdML",
        "gateway": "stripe",
        "gateway_account_id": "gw_16BlyOUaPA0eq12MZ",
        "status": "valid"
      }
    },
    "subscription": {
      "id": "Azq8o5UaLr2WnyHG",
      "billing_period": 1,
      "billing_period_unit": "month",
      "customer_id": "Azq8o5UaLqWWvyGd",
      "status": "active",
      "current_term_start": 1737310793,
      "current_term_end": 1739989193,
      "next_billing_at": 1739989193,
      "created_at": 1737310793,
      "started_at": 1737310793,
      "activated_at": 1737310793,
      "created_from_ip": "205.254.163.189",
      "updated_at": 1737310799,
      "has_scheduled_changes": false,
      "channel": "web",
      "resource_version": 1737310799688,
      "deleted": false,
      "object": "subscription",
      "currency_code": "INR",
      "subscription_items": [
        {
          "item_price_id": "cbdemo_premium-INR-monthly",
          "item_type": "plan",
          "quantity": 1,
          "unit_price": 100000,
          "amount": 0,
          "free_quantity": 3,
          "object": "subscription_item"
        }
      ],
      "shipping_address": {
        "first_name": "test1",
        "last_name": "name",
        "email": "johndoe@gmail.com",
        "company": "johndoe",
        "phone": "+91 83 17 575848",
        "line1": "asdf",
        "line2": "asd",
        "line3": "ahjkd",
        "city": "asdf",
        "state_code": "TG",
        "state": "Telangana",
        "country": "IN",
        "zip": "561432",
        "validation_status": "not_validated",
        "object": "shipping_address"
      },
      "due_invoices_count": 0,
      "mrr": 0,
      "exchange_rate": 1,
      "base_currency_code": "INR",
      "has_scheduled_advance_invoices": false
    }
  },
  "event_type": "payment_failed",
  "webhook_status": "scheduled",
  "webhooks": [
    {
      "id": "whv2_6oZfZUaLmtchA5Bl",
      "webhook_status": "scheduled",
      "object": "webhook"
    }
  ]
}'
```
This should create Payment intent, payment attempt in our system with right values in DB 

Payment intent details in DB 
![Screenshot 2025-03-05 at 12 43 36 PM](https://github.com/user-attachments/assets/a584e694-3c44-48ed-862f-eea8d756ecc3)

Payment attempt details in DB
![Screenshot 2025-03-05 at 12 41 02 PM](https://github.com/user-attachments/assets/3b9762b2-9c60-43c5-be93-646f74cacd38)


Test case 2 : 
change the transaction id in above simulated webhooks and hit the request again, it should create new payment attempt but update the payment intent feature metadata. 
![Screenshot 2025-03-05 at 1 20 35 PM](https://github.com/user-attachments/assets/f1926c83-d670-4c52-ae38-01cd39fb43e6)

Test case 3 : 
After passing succeded  transaction, we should not allow to record further transactions 
![Screenshot 2025-03-05 at 1 25 47 PM](https://github.com/user-attachments/assets/dd9b3540-9289-497d-8b8d-014b7b93b82d)


Note: This above webhook will still not recieve api response it will only create objects in our system .

## 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


Resolution:
<think>
PR Context:
- Title: feat(connector): Add support for passive churn recovery webhooks
- Merged: 2025-02-22T21:44:08Z
- Files changed: 30 files
- Commits: 30 commits

Implementation approach:
## Type of Change
<!-- Put an `x` in the boxes that apply -->

- [ ] Bugfix
- [x] New feature
- [ ] Enhancement
- [ ] Refactoring
- [ ] Dependency updates
- [ ] Documentation
- [ ] CI/CD

## Description
<!-- Describe your changes in detail -->


**Recovery Incoming Webhooks service :** 
Consume incoming webhooks of billing platforms to schedule revenue recovery retries in hs.
**Endpoint :**  
v2/webhooks/revenue_recovery/{{profile_id}}/{{merchant_connector_account_id}}
**Authe
</think>

To implement this, you'll need to modify:


**Detailed Implementation:**


**Commit 1:** feat(connector): Add support for passive churn recovery webhooks (#7109)
Co-authored-by: Chikke Sruj
Files to modify:


**crates/api_models/src/payments.rs**
  Add:
    - struct: pub::PaymentAttemptFeatureMetadata
    - struct: pub::PaymentAttemptRevenueRecoveryData

**crates/api_models/src/webhooks.rs**
  Add:
    - enum: pub::InvoiceIdType

**crates/common_enums/src/enums.rs**
  Add:
    - enum: pub::TriggeredBy

**crates/common_utils/src/ext_traits.rs**
  Add:
    - function: private::async_unwrap_or_else
    - function: private::async_unwrap_or_else

**crates/diesel_models/src/payment_attempt.rs**
  Add:
    - struct: pub::PaymentAttemptFeatureMetadata
    - struct: pub::PaymentAttemptRecoveryData

**crates/diesel_models/src/schema_v2.rs**
  Modify existing code

**crates/hyperswitch_domain_models/src/lib.rs**
  Add:
    - mod: pub::revenue_recovery

**crates/hyperswitch_domain_models/src/payments/payment_attempt.rs**
  Add:
    - function: private::from
    - function: private::from
    - struct: pub::PaymentAttemptFeatureMetadata
    - struct: pub::PaymentAttemptRevenueRecoveryData
    - impl: impl From<&PaymentAttemptFeatureMetadata> for DieselPaymentAttemptFeatureMetadata
    - impl: impl From<DieselPaymentAttemptFeatureMetadata> for PaymentAttemptFeatureMetadata

**crates/hyperswitch_domain_models/src/revenue_recovery.rs**
  Add:
    - function: pub::get_attempt_triggered_by
    - function: pub::get_action
    - function: private::from
    - function: private::from
    - struct: pub::RevenueRecoveryAttemptData
    - struct: pub::RevenueRecoveryInvoiceData
    - struct: pub::RecoveryPaymentIntent
    - struct: pub::RecoveryPaymentAttempt
    - enum: pub::RecoveryAction
    - impl: impl RecoveryPaymentAttempt
    - impl: impl RecoveryAction
    - impl: impl From<&RevenueRecoveryInvoiceData> for api_models::payments::AmountDetails
    - impl: impl From<&RevenueRecoveryInvoiceData> for api_models::payments::PaymentsCreateIntentRequest

**crates/hyperswitch_interfaces/src/webhooks.rs**
  Add:
    - function: private::get_revenue_recovery_attempt_details
    - function: private::get_revenue_recovery_invoice_details

**crates/openapi/src/openapi.rs**
  Modify existing code

**crates/openapi/src/openapi_v2.rs**
  Modify existing code

**crates/router/src/core/errors.rs**
  Add:
    - enum: pub::RevenueRecoveryError

**crates/router/src/core/payments.rs**
  Modify existing code

**crates/router/src/core/payments/transformers.rs**
  Modify existing code

**crates/router/src/core/webhooks.rs**
  Add:
    - mod: private::recovery_incoming

**crates/router/src/core/webhooks/incoming_v2.rs**
  Modify existing code

**crates/router/src/core/webhooks/recovery_incoming.rs**
  Add:
    - function: pub::recovery_incoming_webhook_flow
    - function: private::get_payment_intent
    - function: private::create_payment_intent
    - function: private::get_payment_attempt
    - function: private::record_payment_attempt
    - struct: pub::RevenueRecoveryInvoice
    - struct: pub::RevenueRecoveryAttempt
    - impl: impl RevenueRecoveryInvoice
    - impl: impl RevenueRecoveryAttempt

**crates/router/src/routes.rs**
  Add:
    - mod: pub::recovery_webhooks

**crates/router/src/routes/app.rs**
  Modify existing code

**crates/router/src/routes/lock_utils.rs**
  Modify existing code

**crates/router/src/routes/recovery_webhooks.rs**
  Add:
    - function: pub::recovery_receive_incoming_webhook

**crates/router/src/services/connector_integration_interface.rs**
  Add:
    - function: private::get_revenue_recovery_attempt_details
    - function: private::get_revenue_recovery_invoice_details

**crates/router_env/src/logger/types.rs**
  Modify existing code