Bug: [REFACTOR] Payment Attempt as mandatory field in PaymentStatusData



Payment Attempt as mandatory field in PaymentStatusData as the endpoint /payments/{payment_id} is called after confirmation.
diff --git a/crates/hyperswitch_domain_models/src/payments.rs b/crates/hyperswitch_domain_models/src/payments.rs
index 047fe7fb2cf..42d1ef1a426 100644
--- a/crates/hyperswitch_domain_models/src/payments.rs
+++ b/crates/hyperswitch_domain_models/src/payments.rs
@@ -881,7 +881,7 @@ where
 {
     pub flow: PhantomData<F>,
     pub payment_intent: PaymentIntent,
-    pub payment_attempt: Option<PaymentAttempt>,
+    pub payment_attempt: PaymentAttempt,
     pub payment_address: payment_address::PaymentAddress,
     pub attempts: Option<Vec<PaymentAttempt>>,
     /// Should the payment status be synced with connector
diff --git a/crates/hyperswitch_domain_models/src/router_data.rs b/crates/hyperswitch_domain_models/src/router_data.rs
index 793808b1342..955ddca3662 100644
--- a/crates/hyperswitch_domain_models/src/router_data.rs
+++ b/crates/hyperswitch_domain_models/src/router_data.rs
@@ -1129,11 +1129,7 @@ impl
                     .get_captured_amount(payment_data)
                     .unwrap_or(MinorUnit::zero());
 
-                let total_amount = payment_data
-                    .payment_attempt
-                    .as_ref()
-                    .map(|attempt| attempt.amount_details.get_net_amount())
-                    .unwrap_or(MinorUnit::zero());
+                let total_amount = payment_data.payment_attempt.amount_details.get_net_amount();
 
                 if amount_captured == total_amount {
                     common_enums::AttemptStatus::Charged
@@ -1149,7 +1145,7 @@ impl
         &self,
         payment_data: &payments::PaymentStatusData<router_flow_types::PSync>,
     ) -> Option<MinorUnit> {
-        let payment_attempt = payment_data.payment_attempt.as_ref()?;
+        let payment_attempt = &payment_data.payment_attempt;
 
         // Based on the status of the response, we can determine the amount capturable
         let intent_status = common_enums::IntentStatus::from(self.status);
@@ -1179,7 +1175,7 @@ impl
         &self,
         payment_data: &payments::PaymentStatusData<router_flow_types::PSync>,
     ) -> Option<MinorUnit> {
-        let payment_attempt = payment_data.payment_attempt.as_ref()?;
+        let payment_attempt = &payment_data.payment_attempt;
 
         // Based on the status of the response, we can determine the amount capturable
         let intent_status = common_enums::IntentStatus::from(self.status);
diff --git a/crates/router/src/core/payments.rs b/crates/router/src/core/payments.rs
index bfe7f7962d2..2cc738d263f 100644
--- a/crates/router/src/core/payments.rs
+++ b/crates/router/src/core/payments.rs
@@ -1808,7 +1808,7 @@ pub async fn record_attempt_core(
     let default_payment_status_data = PaymentStatusData {
         flow: PhantomData,
         payment_intent: payment_data.payment_intent.clone(),
-        payment_attempt: Some(payment_data.payment_attempt.clone()),
+        payment_attempt: payment_data.payment_attempt.clone(),
         attempts: None,
         should_sync_with_connector: false,
         payment_address: payment_data.payment_address.clone(),
@@ -1839,7 +1839,7 @@ pub async fn record_attempt_core(
                 payment_data: PaymentStatusData {
                     flow: PhantomData,
                     payment_intent: payment_data.payment_intent.clone(),
-                    payment_attempt: Some(payment_data.payment_attempt.clone()),
+                    payment_attempt: payment_data.payment_attempt.clone(),
                     attempts: None,
                     should_sync_with_connector: true,
                     payment_address: payment_data.payment_address.clone(),
@@ -1863,9 +1863,7 @@ pub async fn record_attempt_core(
     let record_payment_data = domain_payments::PaymentAttemptRecordData {
         flow: PhantomData,
         payment_intent: payment_status_data.payment_intent,
-        payment_attempt: payment_status_data
-            .payment_attempt
-            .unwrap_or(payment_data.payment_attempt.clone()),
+        payment_attempt: payment_status_data.payment_attempt,
         revenue_recovery_data: payment_data.revenue_recovery_data.clone(),
         payment_address: payment_data.payment_address.clone(),
     };
@@ -2730,11 +2728,7 @@ impl PaymentRedirectFlow for PaymentRedirectSync {
         let payment_data = &get_tracker_response.payment_data;
         self.validate_status_for_operation(payment_data.payment_intent.status)?;
 
-        let payment_attempt = payment_data
-            .payment_attempt
-            .as_ref()
-            .ok_or(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable("payment_attempt not found in get_tracker_response")?;
+        let payment_attempt = payment_data.payment_attempt.clone();
 
         let connector = payment_attempt
             .connector
@@ -9112,7 +9106,7 @@ impl<F: Clone> OperationSessionSetters<F> for PaymentConfirmData<F> {
 impl<F: Clone> OperationSessionGetters<F> for PaymentStatusData<F> {
     #[track_caller]
     fn get_payment_attempt(&self) -> &storage::PaymentAttempt {
-        todo!()
+        &self.payment_attempt
     }
     fn get_client_secret(&self) -> &Option<Secret<String>> {
         todo!()
@@ -9241,7 +9235,7 @@ impl<F: Clone> OperationSessionGetters<F> for PaymentStatusData<F> {
     }
 
     fn get_optional_payment_attempt(&self) -> Option<&storage::PaymentAttempt> {
-        self.payment_attempt.as_ref()
+        Some(&self.payment_attempt)
     }
 
     fn get_all_keys_required(&self) -> Option<bool> {
@@ -9262,7 +9256,7 @@ impl<F: Clone> OperationSessionSetters<F> for PaymentStatusData<F> {
         todo!()
     }
     fn set_payment_attempt(&mut self, payment_attempt: storage::PaymentAttempt) {
-        self.payment_attempt = Some(payment_attempt);
+        self.payment_attempt = payment_attempt;
     }
 
     fn set_payment_method_data(&mut self, _payment_method_data: Option<domain::PaymentMethodData>) {
diff --git a/crates/router/src/core/payments/operations/payment_get.rs b/crates/router/src/core/payments/operations/payment_get.rs
index 05536c56296..ebf8205ea9b 100644
--- a/crates/router/src/core/payments/operations/payment_get.rs
+++ b/crates/router/src/core/payments/operations/payment_get.rs
@@ -30,9 +30,41 @@ impl ValidateStatusForOperation for PaymentGet {
     /// Validate if the current operation can be performed on the current status of the payment intent
     fn validate_status_for_operation(
         &self,
-        _intent_status: common_enums::IntentStatus,
+        intent_status: common_enums::IntentStatus,
     ) -> Result<(), errors::ApiErrorResponse> {
-        Ok(())
+        match intent_status {
+            common_enums::IntentStatus::RequiresCapture
+            | common_enums::IntentStatus::RequiresCustomerAction
+            | common_enums::IntentStatus::RequiresMerchantAction
+            | common_enums::IntentStatus::Processing
+            | common_enums::IntentStatus::Succeeded
+            | common_enums::IntentStatus::Failed
+            | common_enums::IntentStatus::PartiallyCapturedAndCapturable
+            | common_enums::IntentStatus::PartiallyCaptured
+            | common_enums::IntentStatus::Cancelled => Ok(()),
+            // These statuses are not valid for this operation
+            common_enums::IntentStatus::RequiresConfirmation
+            | common_enums::IntentStatus::RequiresPaymentMethod => {
+                Err(errors::ApiErrorResponse::PaymentUnexpectedState {
+                    current_flow: format!("{self:?}"),
+                    field_name: "status".to_string(),
+                    current_value: intent_status.to_string(),
+                    states: [
+                        common_enums::IntentStatus::RequiresCapture,
+                        common_enums::IntentStatus::RequiresCustomerAction,
+                        common_enums::IntentStatus::RequiresMerchantAction,
+                        common_enums::IntentStatus::Processing,
+                        common_enums::IntentStatus::Succeeded,
+                        common_enums::IntentStatus::Failed,
+                        common_enums::IntentStatus::PartiallyCapturedAndCapturable,
+                        common_enums::IntentStatus::PartiallyCaptured,
+                        common_enums::IntentStatus::Cancelled,
+                    ]
+                    .map(|enum_value| enum_value.to_string())
+                    .join(", "),
+                })
+            }
+        }
     }
 }
 
@@ -137,22 +169,24 @@ impl<F: Send + Clone + Sync> GetTracker<F, PaymentStatusData<F>, PaymentsRetriev
             .await
             .to_not_found_response(errors::ApiErrorResponse::PaymentNotFound)?;
 
-        let payment_attempt = payment_intent
-            .active_attempt_id
-            .as_ref()
-            .async_map(|active_attempt| async {
-                db.find_payment_attempt_by_id(
-                    key_manager_state,
-                    merchant_context.get_merchant_key_store(),
-                    active_attempt,
-                    storage_scheme,
-                )
-                .await
-                .change_context(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable("Could not find payment attempt given the attempt id")
-            })
+        self.validate_status_for_operation(payment_intent.status)?;
+
+        let active_attempt_id = payment_intent.active_attempt_id.as_ref().ok_or_else(|| {
+            errors::ApiErrorResponse::MissingRequiredField {
+                field_name: ("active_attempt_id"),
+            }
+        })?;
+
+        let payment_attempt = db
+            .find_payment_attempt_by_id(
+                key_manager_state,
+                merchant_context.get_merchant_key_store(),
+                active_attempt_id,
+                storage_scheme,
+            )
             .await
-            .transpose()?;
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Could not find payment attempt given the attempt id")?;
 
         let should_sync_with_connector =
             request.force_sync && payment_intent.status.should_force_sync_with_connector();
@@ -168,9 +202,8 @@ impl<F: Send + Clone + Sync> GetTracker<F, PaymentStatusData<F>, PaymentsRetriev
                 .clone()
                 .map(|address| address.into_inner()),
             payment_attempt
-                .as_ref()
-                .and_then(|payment_attempt| payment_attempt.payment_method_billing_address.as_ref())
-                .cloned()
+                .payment_method_billing_address
+                .clone()
                 .map(|address| address.into_inner()),
             Some(true),
         );
@@ -268,34 +301,37 @@ impl<F: Clone + Send + Sync> Domain<F, PaymentsRetrieveRequest, PaymentStatusDat
         // TODO: do not take the whole payment data here
         payment_data: &mut PaymentStatusData<F>,
     ) -> CustomResult<ConnectorCallType, errors::ApiErrorResponse> {
-        match &payment_data.payment_attempt {
-            Some(payment_attempt) if payment_data.should_sync_with_connector => {
-                let connector = payment_attempt
-                    .connector
-                    .as_ref()
-                    .get_required_value("connector")
-                    .change_context(errors::ApiErrorResponse::InternalServerError)
-                    .attach_printable("Connector is none when constructing response")?;
+        let payment_attempt = &payment_data.payment_attempt;
 
-                let merchant_connector_id = payment_attempt
-                    .merchant_connector_id
-                    .as_ref()
-                    .get_required_value("merchant_connector_id")
-                    .change_context(errors::ApiErrorResponse::InternalServerError)
-                    .attach_printable("Merchant connector id is none when constructing response")?;
+        if payment_data.should_sync_with_connector {
+            let connector = payment_attempt
+                .connector
+                .as_ref()
+                .get_required_value("connector")
+                .change_context(errors::ApiErrorResponse::InternalServerError)
+                .attach_printable("Connector is none when constructing response")?;
 
-                let connector_data = api::ConnectorData::get_connector_by_name(
-                    &state.conf.connectors,
-                    connector,
-                    api::GetToken::Connector,
-                    Some(merchant_connector_id.to_owned()),
-                )
+            let merchant_connector_id = payment_attempt
+                .merchant_connector_id
+                .as_ref()
+                .get_required_value("merchant_connector_id")
                 .change_context(errors::ApiErrorResponse::InternalServerError)
-                .attach_printable("Invalid connector name received")?;
+                .attach_printable("Merchant connector id is none when constructing response")?;
 
-                Ok(ConnectorCallType::PreDetermined(connector_data.into()))
-            }
-            None | Some(_) => Ok(ConnectorCallType::Skip),
+            let connector_data = api::ConnectorData::get_connector_by_name(
+                &state.conf.connectors,
+                connector,
+                api::GetToken::Connector,
+                Some(merchant_connector_id.to_owned()),
+            )
+            .change_context(errors::ApiErrorResponse::InternalServerError)
+            .attach_printable("Invalid connector name received")?;
+
+            Ok(ConnectorCallType::PreDetermined(
+                api::ConnectorRoutingData::from(connector_data),
+            ))
+        } else {
+            Ok(ConnectorCallType::Skip)
         }
     }
 }
diff --git a/crates/router/src/core/payments/operations/payment_response.rs b/crates/router/src/core/payments/operations/payment_response.rs
index da0c397fc9e..40285ee5ce4 100644
--- a/crates/router/src/core/payments/operations/payment_response.rs
+++ b/crates/router/src/core/payments/operations/payment_response.rs
@@ -2593,12 +2593,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentStatusData<F>, types::PaymentsSyncDat
         let payment_attempt_update =
             response_router_data.get_payment_attempt_update(&payment_data, storage_scheme);
 
-        let payment_attempt = payment_data
-            .payment_attempt
-            .ok_or(errors::ApiErrorResponse::InternalServerError)
-            .attach_printable(
-                "Payment attempt not found in payment data in post update trackers",
-            )?;
+        let payment_attempt = payment_data.payment_attempt;
 
         let updated_payment_intent = db
             .update_payment_intent(
@@ -2625,7 +2620,7 @@ impl<F: Clone> PostUpdateTracker<F, PaymentStatusData<F>, types::PaymentsSyncDat
             .attach_printable("Unable to update payment attempt")?;
 
         payment_data.payment_intent = updated_payment_intent;
-        payment_data.payment_attempt = Some(updated_payment_attempt);
+        payment_data.payment_attempt = updated_payment_attempt;
 
         Ok(payment_data)
     }
diff --git a/crates/router/src/core/payments/transformers.rs b/crates/router/src/core/payments/transformers.rs
index 91dc162b912..4dff52acb4c 100644
--- a/crates/router/src/core/payments/transformers.rs
+++ b/crates/router/src/core/payments/transformers.rs
@@ -587,11 +587,7 @@ pub async fn construct_router_data_for_psync<'a>(
         .change_context(errors::ApiErrorResponse::InternalServerError)
         .attach_printable("Failed while parsing value for ConnectorAuthType")?;
 
-    let attempt = &payment_data
-        .payment_attempt
-        .get_required_value("attempt")
-        .change_context(errors::ApiErrorResponse::InternalServerError)
-        .attach_printable("Payment Attempt is not available in payment data")?;
+    let attempt = &payment_data.payment_attempt;
 
     let connector_request_reference_id = payment_intent
         .merchant_reference_id
@@ -1913,21 +1909,19 @@ where
         profile: &domain::Profile,
     ) -> RouterResponse<api_models::payments::PaymentsResponse> {
         let payment_intent = self.payment_intent;
-        let optional_payment_attempt = self.payment_attempt.as_ref();
+        let payment_attempt = &self.payment_attempt;
 
         let amount = api_models::payments::PaymentAmountDetailsResponse::foreign_from((
             &payment_intent.amount_details,
-            optional_payment_attempt.map(|payment_attempt| &payment_attempt.amount_details),
+            &payment_attempt.amount_details,
         ));
 
-        let connector =
-            optional_payment_attempt.and_then(|payment_attempt| payment_attempt.connector.clone());
+        let connector = payment_attempt.connector.clone();
 
-        let merchant_connector_id = optional_payment_attempt
-            .and_then(|payment_attempt| payment_attempt.merchant_connector_id.clone());
+        let merchant_connector_id = payment_attempt.merchant_connector_id.clone();
 
-        let error = optional_payment_attempt
-            .and_then(|payment_attempt| payment_attempt.error.clone())
+        let error = payment_attempt
+            .error
             .as_ref()
             .map(api_models::payments::ErrorDetails::foreign_from);
         let attempts = self.attempts.as_ref().map(|attempts| {
@@ -1949,8 +1943,8 @@ where
 
         let connector_token_details = self
             .payment_attempt
-            .as_ref()
-            .and_then(|attempt| attempt.connector_token_details.clone())
+            .connector_token_details
+            .clone()
             .and_then(Option::<api_models::payments::ConnectorTokenDetails>::foreign_from);
 
         let return_url = payment_intent.return_url.or(profile.return_url.clone());
@@ -1969,31 +1963,16 @@ where
             shipping: self.payment_address.get_shipping().cloned().map(From::from),
             created: payment_intent.created_at,
             payment_method_data,
-            payment_method_type: self
-                .payment_attempt
-                .as_ref()
-                .map(|attempt| attempt.payment_method_type),
-            payment_method_subtype: self
-                .payment_attempt
-                .as_ref()
-                .map(|attempt| attempt.payment_method_subtype),
-            connector_transaction_id: self
-                .payment_attempt
-                .as_ref()
-                .and_then(|attempt| attempt.connector_payment_id.clone()),
+            payment_method_type: Some(payment_attempt.payment_method_type),
+            payment_method_subtype: Some(payment_attempt.payment_method_subtype),
+            connector_transaction_id: payment_attempt.connector_payment_id.clone(),
             connector_reference_id: None,
             merchant_connector_id,
             browser_info: None,
             connector_token_details,
-            payment_method_id: self
-                .payment_attempt
-                .as_ref()
-                .and_then(|attempt| attempt.payment_method_id.clone()),
+            payment_method_id: payment_attempt.payment_method_id.clone(),
             error,
-            authentication_type_applied: self
-                .payment_attempt
-                .as_ref()
-                .and_then(|attempt| attempt.authentication_applied),
+            authentication_type_applied: payment_attempt.authentication_applied,
             authentication_type: payment_intent.authentication_type,
             next_action: None,
             attempts,
diff --git a/crates/router/src/core/revenue_recovery.rs b/crates/router/src/core/revenue_recovery.rs
index d6091d8aa1a..590abcfce21 100644
--- a/crates/router/src/core/revenue_recovery.rs
+++ b/crates/router/src/core/revenue_recovery.rs
@@ -259,10 +259,8 @@ pub async fn perform_payments_sync(
         revenue_recovery_payment_data,
     )
     .await?;
-    // If there is an active_attempt id then there will be a payment attempt
-    let payment_attempt = psync_data
-        .payment_attempt
-        .get_required_value("Payment Attempt")?;
+
+    let payment_attempt = psync_data.payment_attempt;
     let mut revenue_recovery_metadata = payment_intent
         .feature_metadata
         .as_ref()
diff --git a/crates/router/src/core/revenue_recovery/types.rs b/crates/router/src/core/revenue_recovery/types.rs
index e8adf91e07c..6d66a8b500c 100644
--- a/crates/router/src/core/revenue_recovery/types.rs
+++ b/crates/router/src/core/revenue_recovery/types.rs
@@ -229,11 +229,7 @@ impl Decision {
                 .await
                 .change_context(errors::RecoveryError::PaymentCallFailed)
                 .attach_printable("Error while executing the Psync call")?;
-                let payment_attempt = psync_data
-                    .payment_attempt
-                    .get_required_value("Payment Attempt")
-                    .change_context(errors::RecoveryError::ValueNotFound)
-                    .attach_printable("Error while executing the Psync call")?;
+                let payment_attempt = psync_data.payment_attempt;
                 Self::Psync(payment_attempt.status, payment_attempt.get_id().clone())
             }
             (
@@ -250,11 +246,7 @@ impl Decision {
                 .change_context(errors::RecoveryError::PaymentCallFailed)
                 .attach_printable("Error while executing the Psync call")?;
 
-                let payment_attempt = psync_data
-                    .payment_attempt
-                    .get_required_value("Payment Attempt")
-                    .change_context(errors::RecoveryError::ValueNotFound)
-                    .attach_printable("Error while executing the Psync call")?;
+                let payment_attempt = psync_data.payment_attempt;
 
                 let attempt_triggered_by = payment_attempt
                     .feature_metadata
diff --git a/crates/router/src/core/webhooks/incoming_v2.rs b/crates/router/src/core/webhooks/incoming_v2.rs
index b4ac1fbc805..d1207b7cb94 100644
--- a/crates/router/src/core/webhooks/incoming_v2.rs
+++ b/crates/router/src/core/webhooks/incoming_v2.rs
@@ -671,7 +671,7 @@ where
         payment_data: PaymentStatusData {
             flow: PhantomData,
             payment_intent,
-            payment_attempt: Some(payment_attempt),
+            payment_attempt,
             attempts: None,
             should_sync_with_connector: true,
             payment_address,

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

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

## Description
<!-- Describe your changes in detail -->
Currently, we have two endpoints for retrieving payment information in v2:
- GET  /payments/:id
- GET /payments/:id:/get-intent

The endpoint /payments/{payment_id} is called after confirmation, at which point a payment attempt always exists. Currently, payment_attempt in PaymentStatusData is stored as an optional field requiring implementation for get_payment_attempt().
This PR refactors the PaymentStatusData struct to make payment_attempt a required field

### 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. Payments Create
2. Payments Confirm
3. Payments Retrieve

```
curl --location 'http://localhost:8080/v2/payments/12345_pay_0196fd5c3d7a7502a39002fd8ce86a03?force_sync=true' \
--header 'x-profile-id: pro_zpak30UxH06EvzDWoXsW' \
--header 'Authorization: api-key=dev_Qi7ZsoVS4uccUf9Zy3pbS3eqvHjledzayfBn3i74bic6JHGMXNGpuY4wwN1xEMoQ' \
--data ''
```
Response
```
{
    "id": "12345_pay_0196fd5c3d7a7502a39002fd8ce86a03",
    "status": "succeeded",
    "amount": {
        "order_amount": 100,
        "currency": "USD",
        "shipping_cost": null,
        "order_tax_amount": null,
        "external_tax_calculation": "skip",
        "surcharge_calculation": "skip",
        "surcharge_amount": null,
        "tax_on_surcharge": null,
        "net_amount": 100,
        "amount_to_capture": null,
        "amount_capturable": 0,
        "amount_captured": 100
    },
    "customer_id": null,
    "connector": "stripe",
    "created": "2025-05-23T13:36:42.987Z",
    "payment_method_data": {
        "billing": null
    },
    "payment_method_type": "card",
    "payment_method_subtype": "credit",
    "connector_transaction_id": "pi_3RRvmED5R7gDAGff0qqjNSfu",
    "connector_reference_id": null,
    "merchant_connector_id": "mca_8S2I5IjIiW6MnlXUleeZ",
    "browser_info": null,
    "error": null,
    "shipping": {
        "address": {
            "city": "Karwar",
            "country": null,
            "line1": null,
            "line2": null,
            "line3": null,
            "zip": "581301",
            "state": "Karnataka",
            "first_name": "John",
            "last_name": "Dough"
        },
        "phone": null,
        "email": "example@example.com"
    },
    "billing": {
        "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "line3": null,
            "zip": null,
            "state": null,
            "first_name": "John",
            "last_name": "Dough"
        },
        "phone": null,
        "email": "example@example.com"
    },
    "attempts": null,
    "connector_token_details": {
        "token": "pm_1RRvmED5R7gDAGffXf71G5j2",
        "connector_token_request_reference_id": "waAfJPIqPwCZbPN4xC"
    },
    "payment_method_id": null,
    "next_action": null,
    "return_url": "https://google.com/success",
    "authentication_type": "no_three_ds",
    "authentication_type_applied": null,
    "is_iframe_redirection_enabled": null
}
```

1. Payments Create
2. Payments Retrieve
```
curl --location 'http://localhost:8080/v2/payments/12345_pay_01970c2d2a3c71e19ac6e9d20e9b5cb5?force_sync=true' \
--header 'x-profile-id: pro_3gDRiJh3bRKWv7ba0NYE' \
--header 'Authorization: api-key=dev_O5tsf4SGjEusjWDUE56oLxR8HIGNrQJoA4Rxp8LpNYFFNxoxo8j74KexSKWmeRbs' \
--data ''
```
Response
```
{
    "error": {
        "type": "invalid_request",
        "message": "This Payment could not be PaymentGet because it has a status of requires_payment_method. The expected state is requires_capture, requires_customer_action, requires_merchant_action, processing, succeeded, failed, partially_captured_and_capturable, partially_captured, cancelled",
        "code": "IR_14"
    }
}
```
3. Payments Retrieve (get-intent)
```
curl --location 'http://localhost:8080/v2/payments/12345_pay_01970c2d2a3c71e19ac6e9d20e9b5cb5/get-intent' \
--header 'x-profile-id: pro_3gDRiJh3bRKWv7ba0NYE' \
--header 'Authorization: api-key=dev_O5tsf4SGjEusjWDUE56oLxR8HIGNrQJoA4Rxp8LpNYFFNxoxo8j74KexSKWmeRbs' \
--data ''
```
Response
```
{
    "id": "12345_pay_01970c2d2a3c71e19ac6e9d20e9b5cb5",
    "status": "requires_payment_method",
    "amount_details": {
        "order_amount": 100,
        "currency": "USD",
        "shipping_cost": null,
        "order_tax_amount": null,
        "external_tax_calculation": "skip",
        "surcharge_calculation": "skip",
        "surcharge_amount": null,
        "tax_on_surcharge": null
    },
    "client_secret": null,
    "profile_id": "pro_3gDRiJh3bRKWv7ba0NYE",
    "merchant_reference_id": null,
    "routing_algorithm_id": null,
    "capture_method": "automatic",
    "authentication_type": "no_three_ds",
    "billing": {
        "address": {
            "city": null,
            "country": null,
            "line1": null,
            "line2": null,
            "line3": null,
            "zip": null,
            "state": null,
            "first_name": "John",
            "last_name": "Dough"
        },
        "phone": null,
        "email": "example@example.com"
    },
    "shipping": {
        "address": {
            "city": "Karwar",
            "country": null,
            "line1": null,
            "line2": null,
            "line3": null,
            "zip": "581301",
            "state": "Karnataka",
            "first_name": "John",
            "last_name": "Dough"
        },
        "phone": null,
        "email": "example@example.com"
    },
    "customer_id": null,
    "customer_present": "present",
    "description": null,
    "return_url": null,
    "setup_future_usage": "on_session",
    "apply_mit_exemption": "Skip",
    "statement_descriptor": null,
    "order_details": null,
    "allowed_payment_method_types": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "payment_link_enabled": "Skip",
    "payment_link_config": null,
    "request_incremental_authorization": "default",
    "expires_on": "2025-05-26T10:54:36.032Z",
    "frm_metadata": null,
    "request_external_three_ds_authentication": "Skip"
}
```

## 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
