Bug: Accepting PM ID as PM data in /payments



This is to add support for accepting an existing and active PM ID as `payment_method_data` in `/payments` request.
diff --git a/crates/router/src/core/payments/helpers.rs b/crates/router/src/core/payments/helpers.rs
index 51b95041898..c3ab9e00c6d 100644
--- a/crates/router/src/core/payments/helpers.rs
+++ b/crates/router/src/core/payments/helpers.rs
@@ -52,7 +52,9 @@ use crate::{
             self,
             types::{self, AsyncLift},
         },
-        storage::{self, enums as storage_enums, ephemeral_key, CustomerUpdate::Update},
+        storage::{
+            self, enums as storage_enums, ephemeral_key, CardTokenData, CustomerUpdate::Update,
+        },
         transformers::{ForeignFrom, ForeignTryFrom},
         ErrorResponse, MandateReference, RouterData,
     },
@@ -1848,6 +1850,25 @@ pub async fn make_pm_data<'a, F: Clone, R, Ctx: PaymentMethodRetrieve>(
         }
     }
 
+    if payment_data.token_data.is_none() {
+        if let Some(payment_method_info) = &payment_data.payment_method_info {
+            if payment_method_info.payment_method == storage_enums::PaymentMethod::Card {
+                payment_data.token_data =
+                    Some(storage::PaymentTokenData::PermanentCard(CardTokenData {
+                        payment_method_id: Some(payment_method_info.payment_method_id.clone()),
+                        locker_id: payment_method_info
+                            .locker_id
+                            .clone()
+                            .or(Some(payment_method_info.payment_method_id.clone())),
+                        token: payment_method_info
+                            .locker_id
+                            .clone()
+                            .unwrap_or(payment_method_info.payment_method_id.clone()),
+                    }));
+            }
+        }
+    }
+
     // TODO: Handle case where payment method and token both are present in request properly.
     let (payment_method, pm_id) = match (request, payment_data.token_data.as_ref()) {
         (_, Some(hyperswitch_token)) => {

## 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 -->
This is to add support for accepting an existing and active PM ID as payment_method_data in /payments request.

### 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
<!--
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)?
-->
1) Create MCA
2) Copy the profile and hit the below curl to enable pg_agnostic config
```
curl --location 'https://sandbox.hyperswitch.io/routing/business_profile/:business_profile_id/configs/pg_agnostic_mit' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer JWT_Token' \
--data '{
    "enabled": true
}'
```
<img width="939" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/2853f047-97c3-4f44-9f25-a642a49b60a4">
3) Do setup mandate for the pm_id

```
{
    "amount": 100,
    "currency": "USD",
    "confirm": true,
    "profile_id": "pro_RsqZK1Xdmtd9KbAK8YNs",
    "capture_method": "automatic",
    "customer_id": "mandate_customer1",
    "email": "guest@example.com",
    "setup_future_usage": "off_session",
    "customer_acceptance": {
        "acceptance_type": "online"
    },
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number": "4242424242424242",
            "card_exp_month": "03",
            "card_exp_year": "2030",
            "card_holder_name": "Test Holder",
            "card_cvc": "737"
        }
    },
    "payment_type": "setup_mandate",
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    },
    "authentication_type": "no_three_ds"
}
```
<img width="1106" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/20f73b3f-a390-4bb6-a4b0-e7dec320ebf4">

4) Copy the `payment_method_id` from the above response and use it to do a mit 
```
{
    "amount": 499,
    "currency": "USD",
    "confirm": true,
    "profile_id": "pro_RsqZK1Xdmtd9KbAK8YNs",
    "capture_method": "automatic",
    "customer_id": "mandate_customer1",
    "email": "guest@example.com",
    "off_session": true,
    "recurring_details": {
        "type": "payment_method_id",
        "data": "pm_1f5oJ8YVuHkWsxr9nA8t"
    },
    "payment_method": "card",
    "billing": {
        "address": {
            "city": "test",
            "country": "US",
            "line1": "here",
            "line2": "there",
            "line3": "anywhere",
            "zip": "560095",
            "state": "Washington",
            "first_name": "One",
            "last_name": "Two"
        },
        "phone": {
            "number": "1234567890",
            "country_code": "+1"
        },
        "email": "guest@example.com"
    }
}
```
<img width="1129" alt="image" src="https://github.com/juspay/hyperswitch/assets/83439957/94fac52a-52aa-4136-a0fd-e344dd701380">

## 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
- [ ] I added a [CHANGELOG](/CHANGELOG.md) entry if applicable
