Bug: Add invoice number and email in AuthorizeDotNet connector



Add invoice number and email in AuthorizeDotNet connector
diff --git a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
index bb93d9279de..9bd809c124f 100644
--- a/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
+++ b/crates/hyperswitch_connectors/src/connectors/authorizedotnet/transformers.rs
@@ -5,6 +5,7 @@ use common_enums::enums;
 use common_utils::{
     errors::CustomResult,
     ext_traits::{Encode, OptionExt, ValueExt},
+    pii::Email,
     request::Method,
 };
 use error_stack::ResultExt;
@@ -36,6 +37,12 @@ use crate::{
     },
 };
 
+const MAX_ID_LENGTH: usize = 20;
+
+fn get_random_string() -> String {
+    Alphanumeric.sample_string(&mut rand::thread_rng(), MAX_ID_LENGTH)
+}
+
 #[derive(Debug, Serialize)]
 pub enum TransactionType {
     #[serde(rename = "authCaptureTransaction")]
@@ -207,6 +214,7 @@ struct PaymentProfileDetails {
 #[serde(rename_all = "camelCase")]
 pub struct CustomerDetails {
     id: String,
+    email: Option<Email>,
 }
 
 #[derive(Debug, Serialize)]
@@ -230,6 +238,7 @@ pub struct BillTo {
 #[derive(Debug, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Order {
+    invoice_number: String,
     description: String,
 }
 
@@ -611,7 +620,7 @@ impl TryFrom<&AuthorizedotnetRouterData<&PaymentsAuthorizeRouterData>>
         let merchant_authentication =
             AuthorizedotnetAuthType::try_from(&item.router_data.connector_auth_type)?;
 
-        let ref_id = if item.router_data.connector_request_reference_id.len() <= 20 {
+        let ref_id = if item.router_data.connector_request_reference_id.len() <= MAX_ID_LENGTH {
             Some(item.router_data.connector_request_reference_id.clone())
         } else {
             None
@@ -727,9 +736,17 @@ impl
             }),
             profile: None,
             order: Order {
+                invoice_number: get_random_string(),
                 description: item.router_data.connector_request_reference_id.clone(),
             },
-            customer: None,
+            customer: Some(CustomerDetails {
+                id: if item.router_data.payment_id.len() <= MAX_ID_LENGTH {
+                    item.router_data.payment_id.clone()
+                } else {
+                    get_random_string()
+                },
+                email: item.router_data.request.get_optional_email(),
+            }),
             bill_to: item
                 .router_data
                 .get_optional_billing()
@@ -798,9 +815,17 @@ impl
                     })
                 }),
             order: Order {
+                invoice_number: get_random_string(),
                 description: item.router_data.connector_request_reference_id.clone(),
             },
-            customer: None,
+            customer: Some(CustomerDetails {
+                id: if item.router_data.payment_id.len() <= MAX_ID_LENGTH {
+                    item.router_data.payment_id.clone()
+                } else {
+                    get_random_string()
+                },
+                email: item.router_data.request.get_optional_email(),
+            }),
             bill_to: None,
             user_fields: match item.router_data.request.metadata.clone() {
                 Some(metadata) => Some(UserFields {
@@ -835,24 +860,21 @@ impl
             &Card,
         ),
     ) -> Result<Self, Self::Error> {
-        let (profile, customer) = if item.router_data.request.is_mandate_payment() {
-            (
-                Some(ProfileDetails::CreateProfileDetails(CreateProfileDetails {
-                    create_profile: true,
-                })),
-                Some(CustomerDetails {
-                    //The payment ID is included in the customer details because the connector requires unique customer information with a length of fewer than 20 characters when creating a mandate.
-                    //If the length exceeds 20 characters, a random alphanumeric string is used instead.
-                    id: if item.router_data.payment_id.len() <= 20 {
-                        item.router_data.payment_id.clone()
-                    } else {
-                        Alphanumeric.sample_string(&mut rand::thread_rng(), 20)
-                    },
-                }),
-            )
-        } else {
-            (None, None)
-        };
+        let (profile, customer) = (
+            Some(ProfileDetails::CreateProfileDetails(CreateProfileDetails {
+                create_profile: true,
+            })),
+            Some(CustomerDetails {
+                //The payment ID is included in the customer details because the connector requires unique customer information with a length of fewer than 20 characters when creating a mandate.
+                //If the length exceeds 20 characters, a random alphanumeric string is used instead.
+                id: if item.router_data.payment_id.len() <= MAX_ID_LENGTH {
+                    item.router_data.payment_id.clone()
+                } else {
+                    get_random_string()
+                },
+                email: item.router_data.request.get_optional_email(),
+            }),
+        );
         Ok(Self {
             transaction_type: TransactionType::try_from(item.router_data.request.capture_method)?,
             amount: item.amount,
@@ -864,6 +886,7 @@ impl
             })),
             profile,
             order: Order {
+                invoice_number: get_random_string(),
                 description: item.router_data.connector_request_reference_id.clone(),
             },
             customer,
@@ -911,23 +934,19 @@ impl
             &WalletData,
         ),
     ) -> Result<Self, Self::Error> {
-        let (profile, customer) = if item.router_data.request.is_mandate_payment() {
-            (
-                Some(ProfileDetails::CreateProfileDetails(CreateProfileDetails {
-                    create_profile: true,
-                })),
-                Some(CustomerDetails {
-                    id: if item.router_data.payment_id.len() <= 20 {
-                        item.router_data.payment_id.clone()
-                    } else {
-                        Alphanumeric.sample_string(&mut rand::thread_rng(), 20)
-                    },
-                }),
-            )
-        } else {
-            (None, None)
-        };
-
+        let (profile, customer) = (
+            Some(ProfileDetails::CreateProfileDetails(CreateProfileDetails {
+                create_profile: true,
+            })),
+            Some(CustomerDetails {
+                id: if item.router_data.payment_id.len() <= MAX_ID_LENGTH {
+                    item.router_data.payment_id.clone()
+                } else {
+                    get_random_string()
+                },
+                email: item.router_data.request.get_optional_email(),
+            }),
+        );
         Ok(Self {
             transaction_type: TransactionType::try_from(item.router_data.request.capture_method)?,
             amount: item.amount,
@@ -938,6 +957,7 @@ impl
             )?),
             profile,
             order: Order {
+                invoice_number: get_random_string(),
                 description: item.router_data.connector_request_reference_id.clone(),
             },
             customer,

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

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

Closes this [issue](https://github.com/juspay/hyperswitch/issues/7723)

## Description
<!-- Describe your changes in detail -->
Added invoice number and email in AuthorizeDotNet connector

### 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).
-->
This feature is a merchant ask. They need it to claim chargebacks.

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

Postman Tests

1. Connector Create - AuthorizeDotNet

Request:

```
curl --location 'http://localhost:8080/account/merchant_1744026981/connectors' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'api-key: dev_Y3QfvvJdv4BhlHCTUDfWpwTPLyZnFw3nSkJ3pDAOL1ZJvW7foEdck5LuqNh5N1Nj' \
--data '{
    "connector_type": "payment_processor",
    "connector_name": "authorizedotnet",
    "connector_account_details": {
        "auth_type": "BodyKey",
        "api_key": "API KEY",
        "key1": "KEY1"
    },
    "test_mode": false,
    "disabled": false,
    "payment_methods_enabled": [
        {
            "payment_method": "card",
            "payment_method_types": [
                {
                    "payment_method_type": "credit",
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "debit",
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "wallet",
            "payment_method_types": [
                {
                    "payment_method_type": "apple_pay",
                    "payment_experience": "redirect_to_url",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "paypal",
                    "payment_experience": "redirect_to_url",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "pay_later",
            "payment_method_types": [
                {
                    "payment_method_type": "klarna",
                    "payment_experience": "redirect_to_url",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "affirm",
                    "payment_experience": "redirect_to_url",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "afterpay_clearpay",
                    "payment_experience": "redirect_to_url",
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        }
    ],
    "connector_webhook_details": {
        "merchant_secret": "MyWebhookSecret"
    },
    "business_country": "US",
    "business_label": "default",
}'
```
Response:

```
{
    "connector_type": "payment_processor",
    "connector_name": "authorizedotnet",
    "connector_label": "authorizedotnet_US_default",
    "merchant_connector_id": "mca_z9HhKBpL6GKf6kLyTqzO",
    "profile_id": "pro_APc2BIFDjU2pAlUnqbD0",
    "connector_account_details": {
        "auth_type": "BodyKey",
        "api_key": "29*****G3",
        "key1": "34************nv"
    },
    "payment_methods_enabled": [
        {
            "payment_method": "card",
            "payment_method_types": [
                {
                    "payment_method_type": "credit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "debit",
                    "payment_experience": null,
                    "card_networks": [
                        "Visa",
                        "Mastercard"
                    ],
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "wallet",
            "payment_method_types": [
                {
                    "payment_method_type": "apple_pay",
                    "payment_experience": "redirect_to_url",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "paypal",
                    "payment_experience": "redirect_to_url",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        },
        {
            "payment_method": "pay_later",
            "payment_method_types": [
                {
                    "payment_method_type": "klarna",
                    "payment_experience": "redirect_to_url",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "affirm",
                    "payment_experience": "redirect_to_url",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                },
                {
                    "payment_method_type": "afterpay_clearpay",
                    "payment_experience": "redirect_to_url",
                    "card_networks": null,
                    "accepted_currencies": null,
                    "accepted_countries": null,
                    "minimum_amount": 1,
                    "maximum_amount": 68607706,
                    "recurring_enabled": true,
                    "installment_payment_enabled": true
                }
            ]
        }
    ],
    "connector_webhook_details": {
        "merchant_secret": "MyWebhookSecret",
        "additional_secret": null
    },
    "test_mode": false,
    "disabled": false,
    "frm_configs": null,
    "business_country": "US",
    "business_label": "default",
    "business_sub_label": null,
    "applepay_verified_domains": null,
    "pm_auth_config": null,
    "status": "active",
    "additional_merchant_data": null,
}
```

2. Payments - Create

Request:

```
{
    "amount": 35308,
    "currency": "USD",
    "confirm": false,
    "customer_id": "StripeCustomer",
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "phone_country_code": "+1",
    "billing": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country": "US",
            "first_name": "joseph",
            "last_name": "Doe"
        }
    },
    "shipping": {
        "address": {
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "city": "San Fransico",
            "state": "California",
            "zip": "94122",
            "country":"US",
            "first_name": "PiX"
        }
    }
}
```

Response:

```
{
    "payment_id": "pay_AhwRHSRSeXE0HUgmIDh4",
    "merchant_id": "merchant_1744026981",
    "status": "requires_payment_method",
    "amount": 35308,
    "net_amount": 35308,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": null,
    "connector": null,
    "client_secret": "pay_AhwRHSRSeXE0HUgmIDh4_secret_rlGIHoYt8wloO4iCKQmq",
    "created": "2025-04-07T11:56:35.755Z",
    "currency": "USD",
    "customer_id": "StripeCustomer",
    "customer": {
        "id": "StripeCustomer",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": null,
    "payment_method": null,
    "payment_method_data": null,
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": null,
    "authentication_type": null,
    "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": null,
    "connector_label": null,
    "business_country": null,
    "business_label": "default",
    "business_sub_label": null,
    "allowed_payment_method_types": null,
    "ephemeral_key": {
        "customer_id": "StripeCustomer",
        "created_at": 1744026995,
        "expires": 1744030595,
        "secret": "epk_0d1d33b9a9964dd08fda3c8894ea808a"
    },
    "manual_retry_allowed": null,
    "connector_transaction_id": null,
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": null,
    "payment_link": null,
    "profile_id": "pro_APc2BIFDjU2pAlUnqbD0",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": null,
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-04-07T12:11:35.755Z",
    "fingerprint": null,
    "browser_info": null,
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-04-07T11:56:35.794Z",
    "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": null,
    "card_discovery": null,
    "issuer_error_code": null,
    "issuer_error_message": null
}
```

3. Payments - Confirm

Request:

```
{
    "connector": ["authorizedotnet"],
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_type": "credit",
    "payment_method_data": {
        "card": {
            "card_number": "370000000000002",
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "card_cvc": "123"
        }
    }
}
```

Response:

```
{
    "payment_id": "pay_AhwRHSRSeXE0HUgmIDh4",
    "merchant_id": "merchant_1744026981",
    "status": "succeeded",
    "amount": 35308,
    "net_amount": 35308,
    "shipping_cost": null,
    "amount_capturable": 0,
    "amount_received": 35308,
    "connector": "authorizedotnet",
    "client_secret": "pay_AhwRHSRSeXE0HUgmIDh4_secret_rlGIHoYt8wloO4iCKQmq",
    "created": "2025-04-07T11:56:35.755Z",
    "currency": "USD",
    "customer_id": "StripeCustomer",
    "customer": {
        "id": "StripeCustomer",
        "name": "John Doe",
        "email": "guest@example.com",
        "phone": "999999999",
        "phone_country_code": "+1"
    },
    "description": null,
    "refunds": null,
    "disputes": null,
    "mandate_id": null,
    "mandate_data": null,
    "setup_future_usage": null,
    "off_session": null,
    "capture_on": null,
    "capture_method": "automatic",
    "payment_method": "card",
    "payment_method_data": {
        "card": {
            "last4": "0002",
            "card_type": null,
            "card_network": null,
            "card_issuer": null,
            "card_issuing_country": null,
            "card_isin": "370000",
            "card_extended_bin": null,
            "card_exp_month": "10",
            "card_exp_year": "25",
            "card_holder_name": "joseph Doe",
            "payment_checks": null,
            "authentication_data": null
        },
        "billing": null
    },
    "payment_token": null,
    "shipping": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "PiX",
            "last_name": null
        },
        "phone": null,
        "email": null
    },
    "billing": {
        "address": {
            "city": "San Fransico",
            "country": "US",
            "line1": "1467",
            "line2": "Harrison Street",
            "line3": "Harrison Street",
            "zip": "94122",
            "state": "California",
            "first_name": "joseph",
            "last_name": "Doe"
        },
        "phone": null,
        "email": null
    },
    "order_details": null,
    "email": "guest@example.com",
    "name": "John Doe",
    "phone": "999999999",
    "return_url": null,
    "authentication_type": "no_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": null,
    "manual_retry_allowed": false,
    "connector_transaction_id": "120060815605",
    "frm_message": null,
    "metadata": null,
    "connector_metadata": null,
    "feature_metadata": null,
    "reference_id": "120060815605",
    "payment_link": null,
    "profile_id": "pro_APc2BIFDjU2pAlUnqbD0",
    "surcharge_details": null,
    "attempt_count": 1,
    "merchant_decision": null,
    "merchant_connector_id": "mca_z9HhKBpL6GKf6kLyTqzO",
    "incremental_authorization_allowed": null,
    "authorization_count": null,
    "incremental_authorizations": null,
    "external_authentication_details": null,
    "external_3ds_authentication_attempted": false,
    "expires_on": "2025-04-07T12:11:35.755Z",
    "fingerprint": null,
    "browser_info": {
        "os_type": null,
        "language": null,
        "time_zone": null,
        "ip_address": "::1",
        "os_version": null,
        "user_agent": null,
        "color_depth": null,
        "device_model": null,
        "java_enabled": null,
        "screen_width": null,
        "accept_header": null,
        "screen_height": null,
        "accept_language": "en",
        "java_script_enabled": null
    },
    "payment_method_id": null,
    "payment_method_status": null,
    "updated": "2025-04-07T11:56:41.667Z",
    "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": null,
    "card_discovery": "manual",
    "issuer_error_code": null,
    "issuer_error_message": null
}
```

Screenshot of the AuthorizeDotNet Sandbox dashboard:
<img width="1286" alt="Screenshot 2025-04-04 at 6 56 52 PM" src="https://github.com/user-attachments/assets/2ca2bc0f-4f01-4aac-b97b-cdecc57646c6" />

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