}

#[derive(Deserialize, Debug, Serialize)]
pub struct ActionResponse {
    #[serde(rename = "id")]
    pub action_id: String,
    pub amount: MinorUnit,
    #[serde(rename = "type")]
    pub action_type: ActionType,
    pub approved: Option<bool>,
    pub reference: Option<String>,
}

impl From<&ActionResponse> for enums::RefundStatus {
    fn from(item: &ActionResponse) -> Self {
        match item.approved {
            Some(true) => Self::Success,
            Some(false) => Self::Failure,
            None => Self::Pending,
        }
    }
}

impl utils::MultipleCaptureSyncResponse for ActionResponse {
    fn get_connector_capture_id(&self) -> String {
        self.action_id.clone()
    }

    fn get_capture_attempt_status(&self) -> AttemptStatus {
        match self.approved {
            Some(true) => AttemptStatus::Charged,
            Some(false) => AttemptStatus::Failure,
            None => AttemptStatus::Pending,
        }
    }

    fn get_connector_reference_id(&self) -> Option<String> {
        self.reference.clone()
    }

    fn is_capture_response(&self) -> bool {
        self.action_type == ActionType::Capture
    }

    fn get_amount_captured(&self) -> Result<Option<MinorUnit>, error_stack::Report<ParsingError>> {
        Ok(Some(self.amount))
    }
}

impl utils::MultipleCaptureSyncResponse for Box<PaymentsResponse> {
    fn get_connector_capture_id(&self) -> String {
        self.action_id.clone().unwrap_or("".into())
    }

    fn get_capture_attempt_status(&self) -> AttemptStatus {
        get_attempt_status_bal((self.status.clone(), self.balances.clone()))
    }

    fn get_connector_reference_id(&self) -> Option<String> {
        self.reference.clone()
    }

    fn is_capture_response(&self) -> bool {
        self.status == CheckoutPaymentStatus::Captured
    }
    fn get_amount_captured(&self) -> Result<Option<MinorUnit>, error_stack::Report<ParsingError>> {
        Ok(self.amount)
    }
}

#[derive(Debug, Clone, serde::Deserialize, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum CheckoutRedirectResponseStatus {
    Success,
    Failure,
}

#[derive(Debug, Clone, serde::Deserialize, Eq, PartialEq)]
pub struct CheckoutRedirectResponse {
    pub status: Option<CheckoutRedirectResponseStatus>,
    #[serde(rename = "cko-session-id")]
    pub cko_session_id: Option<String>,
}

impl TryFrom<RefundsResponseRouterData<Execute, &ActionResponse>> for RefundsRouterData<Execute> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, &ActionResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response);
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.action_id.clone(),
                refund_status,
            }),
            ..item.data
        })
    }
}

impl TryFrom<RefundsResponseRouterData<RSync, &ActionResponse>> for RefundsRouterData<RSync> {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, &ActionResponse>,
    ) -> Result<Self, Self::Error> {
        let refund_status = enums::RefundStatus::from(item.response);
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.action_id.clone(),
                refund_status,
            }),
            ..item.data
        })
    }
}

impl From<CheckoutRedirectResponseStatus> for AttemptStatus {
    fn from(item: CheckoutRedirectResponseStatus) -> Self {
        match item {
            CheckoutRedirectResponseStatus::Success => Self::AuthenticationSuccessful,
            CheckoutRedirectResponseStatus::Failure => Self::Failure,
        }
    }
}

pub fn is_refund_event(event_code: &CheckoutWebhookEventType) -> bool {
    matches!(
        event_code,
        CheckoutWebhookEventType::PaymentRefunded | CheckoutWebhookEventType::PaymentRefundDeclined
    )
}

pub fn is_chargeback_event(event_code: &CheckoutWebhookEventType) -> bool {
    matches!(
        event_code,
        CheckoutWebhookEventType::DisputeReceived
            | CheckoutWebhookEventType::DisputeExpired
            | CheckoutWebhookEventType::DisputeAccepted
            | CheckoutWebhookEventType::DisputeCanceled
            | CheckoutWebhookEventType::DisputeEvidenceSubmitted
            | CheckoutWebhookEventType::DisputeEvidenceAcknowledgedByScheme
            | CheckoutWebhookEventType::DisputeEvidenceRequired
            | CheckoutWebhookEventType::DisputeArbitrationLost
            | CheckoutWebhookEventType::DisputeArbitrationWon
            | CheckoutWebhookEventType::DisputeWon
            | CheckoutWebhookEventType::DisputeLost
    )
}

#[derive(Debug, Deserialize, strum::Display, Clone)]
#[serde(rename_all = "snake_case")]
pub enum CheckoutWebhookEventType {
    AuthenticationStarted,
    AuthenticationApproved,
    AuthenticationAttempted,
    AuthenticationExpired,
    AuthenticationFailed,
    PaymentApproved,
    PaymentCaptured,
    PaymentDeclined,
    PaymentRefunded,
    PaymentRefundDeclined,
    PaymentAuthenticationFailed,
    PaymentCanceled,
    PaymentCaptureDeclined,
    PaymentVoided,
    DisputeReceived,
    DisputeExpired,
    DisputeAccepted,
    DisputeCanceled,
    DisputeEvidenceSubmitted,
    DisputeEvidenceAcknowledgedByScheme,
    DisputeEvidenceRequired,
    DisputeArbitrationLost,
    DisputeArbitrationWon,
    DisputeWon,
    DisputeLost,
    #[serde(other)]
    Unknown,
}

#[derive(Debug, Deserialize)]
pub struct CheckoutWebhookEventTypeBody {
    #[serde(rename = "type")]
    pub transaction_type: CheckoutWebhookEventType,
}

#[derive(Debug, Deserialize)]
pub struct CheckoutWebhookData {
    pub id: String,
    pub payment_id: Option<String>,
    pub action_id: Option<String>,
    pub reference: Option<String>,
    pub amount: MinorUnit,
    pub balances: Option<Balances>,
    pub response_code: Option<String>,
    pub response_summary: Option<String>,
    pub currency: String,
    pub processed_on: Option<String>,
    pub approved: Option<bool>,
}

#[derive(Debug, Deserialize)]
pub struct CheckoutWebhookBody {
    #[serde(rename = "type")]
    pub transaction_type: CheckoutWebhookEventType,
    pub data: CheckoutWebhookData,
    #[serde(rename = "_links")]
    pub links: Links,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct CheckoutDisputeWebhookData {
    pub id: String,
    pub payment_id: Option<String>,
    pub action_id: Option<String>,
    pub amount: MinorUnit,
    pub currency: enums::Currency,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub evidence_required_by: Option<PrimitiveDateTime>,
    pub reason_code: Option<String>,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub date: Option<PrimitiveDateTime>,
}
#[derive(Debug, Deserialize)]
pub struct CheckoutDisputeWebhookBody {
    #[serde(rename = "type")]
    pub transaction_type: CheckoutDisputeTransactionType,
    pub data: CheckoutDisputeWebhookData,
    #[serde(default, with = "common_utils::custom_serde::iso8601::option")]
    pub created_on: Option<PrimitiveDateTime>,
}
#[derive(Debug, Deserialize, strum::Display, Clone)]
#[serde(rename_all = "snake_case")]
pub enum CheckoutDisputeTransactionType {
    DisputeReceived,
    DisputeExpired,
    DisputeAccepted,
    DisputeCanceled,
    DisputeEvidenceSubmitted,
    DisputeEvidenceAcknowledgedByScheme,
    DisputeEvidenceRequired,
    DisputeArbitrationLost,
    DisputeArbitrationWon,
    DisputeWon,
    DisputeLost,
}

impl From<CheckoutWebhookEventType> for api_models::webhooks::IncomingWebhookEvent {
    fn from(transaction_type: CheckoutWebhookEventType) -> Self {
        match transaction_type {
            CheckoutWebhookEventType::AuthenticationStarted
            | CheckoutWebhookEventType::AuthenticationApproved
            | CheckoutWebhookEventType::AuthenticationAttempted => Self::EventNotSupported,
            CheckoutWebhookEventType::AuthenticationExpired
            | CheckoutWebhookEventType::AuthenticationFailed
            | CheckoutWebhookEventType::PaymentAuthenticationFailed => {
                Self::PaymentIntentAuthorizationFailure
            }
            CheckoutWebhookEventType::PaymentApproved => Self::EventNotSupported,
            CheckoutWebhookEventType::PaymentCaptured => Self::PaymentIntentSuccess,
            CheckoutWebhookEventType::PaymentDeclined => Self::PaymentIntentFailure,
            CheckoutWebhookEventType::PaymentRefunded => Self::RefundSuccess,
            CheckoutWebhookEventType::PaymentRefundDeclined => Self::RefundFailure,
            CheckoutWebhookEventType::PaymentCanceled => Self::PaymentIntentCancelFailure,
            CheckoutWebhookEventType::PaymentCaptureDeclined => Self::PaymentIntentCaptureFailure,
            CheckoutWebhookEventType::PaymentVoided => Self::PaymentIntentCancelled,
            CheckoutWebhookEventType::DisputeReceived
            | CheckoutWebhookEventType::DisputeEvidenceRequired => Self::DisputeOpened,
            CheckoutWebhookEventType::DisputeExpired => Self::DisputeExpired,
            CheckoutWebhookEventType::DisputeAccepted => Self::DisputeAccepted,
            CheckoutWebhookEventType::DisputeCanceled => Self::DisputeCancelled,
            CheckoutWebhookEventType::DisputeEvidenceSubmitted
            | CheckoutWebhookEventType::DisputeEvidenceAcknowledgedByScheme => {
                Self::DisputeChallenged
            }
            CheckoutWebhookEventType::DisputeWon
            | CheckoutWebhookEventType::DisputeArbitrationWon => Self::DisputeWon,
            CheckoutWebhookEventType::DisputeLost
            | CheckoutWebhookEventType::DisputeArbitrationLost => Self::DisputeLost,
            CheckoutWebhookEventType::Unknown => Self::EventNotSupported,
        }
    }
}

impl From<CheckoutDisputeTransactionType> for api_models::enums::DisputeStage {
    fn from(code: CheckoutDisputeTransactionType) -> Self {
        match code {
            CheckoutDisputeTransactionType::DisputeArbitrationLost
            | CheckoutDisputeTransactionType::DisputeArbitrationWon => Self::PreArbitration,
            CheckoutDisputeTransactionType::DisputeReceived
            | CheckoutDisputeTransactionType::DisputeExpired
            | CheckoutDisputeTransactionType::DisputeAccepted
            | CheckoutDisputeTransactionType::DisputeCanceled
            | CheckoutDisputeTransactionType::DisputeEvidenceSubmitted
            | CheckoutDisputeTransactionType::DisputeEvidenceAcknowledgedByScheme
            | CheckoutDisputeTransactionType::DisputeEvidenceRequired
            | CheckoutDisputeTransactionType::DisputeWon
            | CheckoutDisputeTransactionType::DisputeLost => Self::Dispute,
        }
    }
}

#[derive(Debug, Deserialize)]
pub struct CheckoutWebhookObjectResource {
    pub data: serde_json::Value,
}

pub fn construct_file_upload_request(
    file_upload_router_data: UploadFileRouterData,
) -> CustomResult<reqwest::multipart::Form, errors::ConnectorError> {
    let request = file_upload_router_data.request;
    let mut multipart = reqwest::multipart::Form::new();
    multipart = multipart.text("purpose", "dispute_evidence");
    let file_data = reqwest::multipart::Part::bytes(request.file)
        .file_name(format!(
            "{}.{}",
            request.file_key,
            request
                .file_type
                .as_ref()
                .split('/')
                .next_back()
                .unwrap_or_default()
        ))
        .mime_str(request.file_type.as_ref())
        .change_context(errors::ConnectorError::RequestEncodingFailed)
        .attach_printable("Failure in constructing file data")?;
    multipart = multipart.part("file", file_data);
    Ok(multipart)
}

#[derive(Debug, Deserialize, Serialize)]
pub struct FileUploadResponse {
    #[serde(rename = "id")]
    pub file_id: String,
}

#[derive(Default, Debug, Serialize)]
pub struct Evidence {
    pub proof_of_delivery_or_service_file: Option<String>,
    pub invoice_or_receipt_file: Option<String>,
    pub invoice_showing_distinct_transactions_file: Option<String>,
    pub customer_communication_file: Option<String>,
    pub refund_or_cancellation_policy_file: Option<String>,
    pub recurring_transaction_agreement_file: Option<String>,
    pub additional_evidence_file: Option<String>,
}

impl TryFrom<&webhooks::IncomingWebhookRequestDetails<'_>> for PaymentsResponse {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> Result<Self, Self::Error> {
        let details: CheckoutWebhookBody = request
            .body
            .parse_struct("CheckoutWebhookBody")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let data = details.data;
        let psync_struct = Self {
            id: data.payment_id.unwrap_or(data.id),
            amount: Some(data.amount),
            status: CheckoutPaymentStatus::try_from(details.transaction_type)?,
            links: details.links,
            balances: data.balances,
            reference: data.reference,
            response_code: data.response_code,
            response_summary: data.response_summary,
            action_id: data.action_id,
            currency: Some(data.currency),
            processed_on: data.processed_on,
            approved: data.approved,
        };

        Ok(psync_struct)
    }
}

impl TryFrom<&webhooks::IncomingWebhookRequestDetails<'_>> for RefundResponse {
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> Result<Self, Self::Error> {
        let details: CheckoutWebhookBody = request
            .body
            .parse_struct("CheckoutWebhookBody")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;
        let data = details.data;
        let refund_struct = Self {
            action_id: data
                .action_id
                .ok_or(errors::ConnectorError::WebhookBodyDecodingFailed)?,
            reference: data
                .reference
                .ok_or(errors::ConnectorError::WebhookBodyDecodingFailed)?,
        };

        Ok(refund_struct)
    }
}

impl TryFrom<&SubmitEvidenceRouterData> for Evidence {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &SubmitEvidenceRouterData) -> Result<Self, Self::Error> {
        let submit_evidence_request_data = item.request.clone();
        Ok(Self {
            proof_of_delivery_or_service_file: submit_evidence_request_data
                .shipping_documentation_provider_file_id,
            invoice_or_receipt_file: submit_evidence_request_data.receipt_provider_file_id,
            invoice_showing_distinct_transactions_file: submit_evidence_request_data
                .invoice_showing_distinct_transactions_provider_file_id,
            customer_communication_file: submit_evidence_request_data
                .customer_communication_provider_file_id,
            refund_or_cancellation_policy_file: submit_evidence_request_data
                .refund_policy_provider_file_id,
            recurring_transaction_agreement_file: submit_evidence_request_data
                .recurring_transaction_agreement_provider_file_id,
            additional_evidence_file: submit_evidence_request_data
                .uncategorized_file_provider_file_id,
        })
    }
}

impl From<String> for utils::ErrorCodeAndMessage {
    fn from(error: String) -> Self {
        Self {
            error_code: error.clone(),
            error_message: error,
        }
    }
}


pub struct ThreeDsChallenge {
    pub reference: String,
    pub url: Url,
    pub jwt: Secret<String>,
    pub payload: Secret<String>,
}


    state: &'a SessionState,
    connector: &'a api::ConnectorData,
    customer: &'a Option<domain::Customer>,
    merchant_connector_id: &'a domain::MerchantConnectorAccountTypeDetails,
) -> (bool, Option<&'a str>) {
    // Check if create customer is required for the connector
    match enums::PayoutConnectors::try_from(connector.connector_name) {
        Ok(connector) => {
            let connector_needs_customer = state
                .conf
                .connector_customer
                .payout_connector_list
                .contains(&connector);

            if connector_needs_customer {
                let connector_customer_details = customer
                    .as_ref()
                    .and_then(|customer| customer.get_connector_customer_id(merchant_connector_id));
                let should_call_connector = connector_customer_details.is_none();
                (should_call_connector, connector_customer_details)
            } else {
                (false, None)
            }
        }
        _ => (false, None),
    }
}

pub async fn get_gsm_record(
    state: &SessionState,
    error_code: Option<String>,
    error_message: Option<String>,
    connector_name: Option<String>,
    flow: &str,
) -> Option<hyperswitch_domain_models::gsm::GatewayStatusMap> {
    let connector_name = connector_name.unwrap_or_default();
    let get_gsm = || async {
        state.store.find_gsm_rule(
                connector_name.clone(),
                flow.to_string(),
                "sub_flow".to_string(),
                error_code.clone().unwrap_or_default(), // TODO: make changes in connector to get a mandatory code in case of success or error response
                error_message.clone().unwrap_or_default(),
            )
            .await
            .map_err(|err| {
                if err.current_context().is_db_not_found() {
                    logger::warn!(
                        "GSM miss for connector - {}, flow - {}, error_code - {:?}, error_message - {:?}",
                        connector_name,
                        flow,
                        error_code,
                        error_message
                    );
                    metrics::AUTO_PAYOUT_RETRY_GSM_MISS_COUNT.add( 1, &[]);
                } else {
                    metrics::AUTO_PAYOUT_RETRY_GSM_FETCH_FAILURE_COUNT.add( 1, &[]);
                };
                err.change_context(errors::ApiErrorResponse::InternalServerError)
                    .attach_printable("failed to fetch decision from gsm")
            })
    };
    get_gsm()
        .await
        .inspect_err(|err| {
            // warn log should suffice here because we are not propagating this error
            logger::warn!(get_gsm_decision_fetch_error=?err, "error fetching gsm decision");
        })
        .ok()
}

pub fn is_payout_initiated(status: api_enums::PayoutStatus) -> bool {
    !matches!(
        status,
        api_enums::PayoutStatus::RequiresCreation
            | api_enums::PayoutStatus::RequiresConfirmation
            | api_enums::PayoutStatus::RequiresPayoutMethodData
            | api_enums::PayoutStatus::RequiresVendorAccountCreation
            | api_enums::PayoutStatus::Initiated
    )
}

pub(crate) fn validate_payout_status_against_not_allowed_statuses(
    payout_status: api_enums::PayoutStatus,
    not_allowed_statuses: &[api_enums::PayoutStatus],
    action: &'static str,
) -> Result<(), errors::ApiErrorResponse> {
    fp_utils::when(not_allowed_statuses.contains(&payout_status), || {
        Err(errors::ApiErrorResponse::PreconditionFailed {
            message: format!(
                "You cannot {action} this payout because it has status {payout_status}",
            ),
        })
    })
}

pub fn is_payout_terminal_state(status: api_enums::PayoutStatus) -> bool {
    !matches!(
        status,
        api_enums::PayoutStatus::RequiresCreation
            | api_enums::PayoutStatus::RequiresConfirmation
            | api_enums::PayoutStatus::RequiresPayoutMethodData
            | api_enums::PayoutStatus::RequiresVendorAccountCreation
            // Initiated by the underlying connector
            | api_enums::PayoutStatus::Pending
            | api_enums::PayoutStatus::Initiated
            | api_enums::PayoutStatus::RequiresFulfillment
    )
}

pub fn should_call_retrieve(status: api_enums::PayoutStatus) -> bool {
    matches!(
        status,
        api_enums::PayoutStatus::Pending | api_enums::PayoutStatus::Initiated
    )
}

pub fn is_payout_err_state(status: api_enums::PayoutStatus) -> bool {
    matches!(
        status,
        api_enums::PayoutStatus::Cancelled
            | api_enums::PayoutStatus::Failed
            | api_enums::PayoutStatus::Ineligible
    )
}

pub fn is_eligible_for_local_payout_cancellation(status: api_enums::PayoutStatus) -> bool {
    matches!(
        status,
        api_enums::PayoutStatus::RequiresCreation
            | api_enums::PayoutStatus::RequiresConfirmation
            | api_enums::PayoutStatus::RequiresPayoutMethodData
            | api_enums::PayoutStatus::RequiresVendorAccountCreation
    )
}

#[cfg(feature = "olap")]
pub(super) async fn filter_by_constraints(
    db: &dyn StorageInterface,
    constraints: &api::PayoutListConstraints,
    merchant_id: &id_type::MerchantId,
    storage_scheme: storage::enums::MerchantStorageScheme,
) -> CustomResult<Vec<storage::Payouts>, errors::StorageError> {
    let result = db
        .filter_payouts_by_constraints(merchant_id, &constraints.clone().into(), storage_scheme)
        .await?;
    Ok(result)
}

#[cfg(feature = "v2")]
pub async fn update_payouts_and_payout_attempt(
    _payout_data: &mut PayoutData,
    _merchant_context: &domain::MerchantContext,
    _req: &payouts::PayoutCreateRequest,
    _state: &SessionState,
) -> CustomResult<(), errors::ApiErrorResponse> {
    todo!()
}

#[cfg(feature = "v1")]
pub async fn update_payouts_and_payout_attempt(
    payout_data: &mut PayoutData,
    merchant_context: &domain::MerchantContext,
    req: &payouts::PayoutCreateRequest,
    state: &SessionState,
) -> CustomResult<(), errors::ApiErrorResponse> {
    let payout_attempt = payout_data.payout_attempt.to_owned();
    let status = payout_attempt.status;
    let payout_id = payout_attempt.payout_id.clone();
    // Verify update feasibility
    if is_payout_terminal_state(status) || is_payout_initiated(status) {
        return Err(report!(errors::ApiErrorResponse::InvalidRequestData {
            message: format!(
                "Payout {} cannot be updated for status {status}",
                payout_id.get_string_repr()
            ),
        }));
    }

    // Fetch customer details from request and create new or else use existing customer that was attached
    let customer = get_customer_details_from_request(req);
    let customer_id = if customer.customer_id.is_some()
        || customer.name.is_some()
        || customer.email.is_some()
        || customer.phone.is_some()
        || customer.phone_country_code.is_some()
    {
        payout_data.customer_details =
            get_or_create_customer_details(state, &customer, merchant_context).await?;
        payout_data
            .customer_details
            .as_ref()
            .map(|customer| customer.customer_id.clone())
    } else {
        payout_data.payouts.customer_id.clone()
    };

    let (billing_address, address_id) = resolve_billing_address_for_payout(
        state,
        req.billing.as_ref(),
        payout_data.payouts.address_id.as_ref(),
        payout_data.payment_method.as_ref(),
        merchant_context,
        customer_id.as_ref(),
        &payout_id,
    )
    .await?;

    // 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();
    let amount = MinorUnit::from(req.amount.unwrap_or(payouts.amount.into()));
    let updated_payouts = storage::PayoutsUpdate::Update {
        amount,
        destination_currency: req
            .currency
            .to_owned()
            .unwrap_or(payouts.destination_currency),
        source_currency: req.currency.to_owned().unwrap_or(payouts.source_currency),
        description: req
            .description
            .to_owned()
            .clone()
            .or(payouts.description.clone()),
        recurring: req.recurring.to_owned().unwrap_or(payouts.recurring),
        auto_fulfill: req.auto_fulfill.to_owned().unwrap_or(payouts.auto_fulfill),
        return_url: req
            .return_url
            .to_owned()
            .clone()
            .or(payouts.return_url.clone()),
        entity_type: req.entity_type.to_owned().unwrap_or(payouts.entity_type),
        metadata: req.metadata.clone().or(payouts.metadata.clone()),
        status: Some(status),
        profile_id: Some(payout_attempt.profile_id.clone()),
        confirm: req.confirm.to_owned(),
        payout_type: req
            .payout_type
            .to_owned()
            .or(payouts.payout_type.to_owned()),
        address_id: address_id.clone(),
        customer_id: customer_id.clone(),
    };
    let db = &*state.store;
    payout_data.payouts = db
        .update_payout(
            &payouts,
            updated_payouts,
            &payout_attempt,
            merchant_context.get_merchant_account().storage_scheme,
        )
        .await
        .change_context(errors::ApiErrorResponse::InternalServerError)
        .attach_printable("Error updating payouts")?;
    let updated_business_country =
        payout_attempt
            .business_country
            .map_or(req.business_country.to_owned(), |c| {
                req.business_country
                    .to_owned()
                    .and_then(|nc| if nc != c { Some(nc) } else { None })
            });
    let updated_business_label =
        payout_attempt
            .business_label
            .map_or(req.business_label.to_owned(), |l| {
                req.business_label
                    .to_owned()
                    .and_then(|nl| if nl != l { Some(nl) } else { None })
            });
    if updated_business_country.is_some()
        || updated_business_label.is_some()
        || customer_id.is_some()
        || address_id.is_some()
    {
        let payout_attempt = &payout_data.payout_attempt;
        let updated_payout_attempt = storage::PayoutAttemptUpdate::BusinessUpdate {
            business_country: updated_business_country,
            business_label: updated_business_label,
            address_id,
            customer_id,
        };
        payout_data.payout_attempt = db
            .update_payout_attempt(
                payout_attempt,
                updated_payout_attempt,
                &payout_data.payouts,
                merchant_context.get_merchant_account().storage_scheme,
            )
            .await
            .change_context(errors::ApiErrorResponse::InternalServerError)
            .attach_printable("Error updating payout_attempt")?;
    }
    Ok(())
}

pub(super) fn get_customer_details_from_request(
    request: &payouts::PayoutCreateRequest,
) -> CustomerDetails {
    let customer_id = request.get_customer_id().map(ToOwned::to_owned);

    let customer_name = request
        .customer
        .as_ref()
        .and_then(|customer_details| customer_details.name.clone())
        .or(request.name.clone());

    let customer_email = request
        .customer
        .as_ref()
        .and_then(|customer_details| customer_details.email.clone())
        .or(request.email.clone());

    let customer_phone = request
        .customer
        .as_ref()
        .and_then(|customer_details| customer_details.phone.clone())
        .or(request.phone.clone());

    let customer_phone_code = request
        .customer
        .as_ref()
        .and_then(|customer_details| customer_details.phone_country_code.clone())
        .or(request.phone_country_code.clone());

    let tax_registration_id = request
        .customer
        .as_ref()
        .and_then(|customer_details| customer_details.tax_registration_id.clone());

    CustomerDetails {
        customer_id,
        name: customer_name,
        email: customer_email,
        phone: customer_phone,
        phone_country_code: customer_phone_code,
        tax_registration_id,
    }
}

pub async fn get_translated_unified_code_and_message(
    state: &SessionState,
    unified_code: Option<&UnifiedCode>,
    unified_message: Option<&UnifiedMessage>,
    locale: &str,
) -> CustomResult<Option<UnifiedMessage>, errors::ApiErrorResponse> {
    Ok(unified_code
        .zip(unified_message)
        .async_and_then(|(code, message)| async {
            payment_helpers::get_unified_translation(
                state,
                code.0.clone(),
                message.0.clone(),
                locale.to_string(),
            )
            .await
            .map(UnifiedMessage::try_from)
        })
        .await
        .transpose()
        .change_context(errors::ApiErrorResponse::InvalidDataValue {
            field_name: "unified_message",
        })?
        .or_else(|| unified_message.cloned()))
}

pub async fn get_additional_payout_data(
    pm_data: &api::PayoutMethodData,
    db: &dyn StorageInterface,
    profile_id: &id_type::ProfileId,
) -> Option<payout_additional::AdditionalPayoutMethodData> {
    match pm_data {
        api::PayoutMethodData::Card(card_data) => {
            let card_isin = Some(card_data.card_number.get_card_isin());
            let enable_extended_bin =db
            .find_config_by_key_unwrap_or(
                format!("{}_enable_extended_card_bin", profile_id.get_string_repr()).as_str(),
             Some("false".to_string()))
            .await.map_err(|err| services::logger::error!(message="Failed to fetch the config", extended_card_bin_error=?err)).ok();

            let card_extended_bin = match enable_extended_bin {
                Some(config) if config.config == "true" => {
                    Some(card_data.card_number.get_extended_card_bin())
                }
                _ => None,
            };
            let last4 = Some(card_data.card_number.get_last4());

            let card_info = card_isin
                .clone()
                .async_and_then(|card_isin| async move {
                    db.get_card_info(&card_isin)
                        .await
                        .map_err(|error| services::logger::warn!(card_info_error=?error))
                        .ok()
                })
                .await
                .flatten()
                .map(|card_info| {
                    payout_additional::AdditionalPayoutMethodData::Card(Box::new(
                        payout_additional::CardAdditionalData {
                            card_issuer: card_info.card_issuer,
                            card_network: card_info.card_network.clone(),
                            bank_code: card_info.bank_code,
                            card_type: card_info.card_type,
                            card_issuing_country: card_info.card_issuing_country,
                            last4: last4.clone(),
                            card_isin: card_isin.clone(),
                            card_extended_bin: card_extended_bin.clone(),
                            card_exp_month: Some(card_data.expiry_month.clone()),
                            card_exp_year: Some(card_data.expiry_year.clone()),
                            card_holder_name: card_data.card_holder_name.clone(),
                        },
                    ))
                });
            Some(card_info.unwrap_or_else(|| {
                payout_additional::AdditionalPayoutMethodData::Card(Box::new(
                    payout_additional::CardAdditionalData {
                        card_issuer: None,
                        card_network: None,
                        bank_code: None,
                        card_type: None,
                        card_issuing_country: None,
                        last4,
                        card_isin,
                        card_extended_bin,
                        card_exp_month: Some(card_data.expiry_month.clone()),
                        card_exp_year: Some(card_data.expiry_year.clone()),
                        card_holder_name: card_data.card_holder_name.clone(),
                    },
                ))
            }))
        }
        api::PayoutMethodData::Bank(bank_data) => {
            Some(payout_additional::AdditionalPayoutMethodData::Bank(
                Box::new(bank_data.to_owned().into()),
            ))
        }
        api::PayoutMethodData::Wallet(wallet_data) => {
            Some(payout_additional::AdditionalPayoutMethodData::Wallet(
                Box::new(wallet_data.to_owned().into()),
            ))
        }
    }
}

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)),
    }
}


File: crates/hyperswitch_connectors/src/connectors/jpmorgan/transformers.rs
 Public functions: 1
 Public structs: 34

use std::str::FromStr;

use common_enums::enums::CaptureMethod;
use common_utils::types::MinorUnit;
use error_stack::{report, ResultExt};
use hyperswitch_domain_models::{
    payment_method_data::PaymentMethodData,
    router_data::{AccessToken, ConnectorAuthType, RouterData},
    router_flow_types::refunds::{Execute, RSync},
    router_request_types::{PaymentsCancelData, ResponseId},
    router_response_types::{PaymentsResponseData, RefundsResponseData},
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        RefreshTokenRouterData, RefundsRouterData,
    },
};
use hyperswitch_interfaces::errors;
use masking::{PeekInterface, Secret};
use serde::{Deserialize, Serialize};

use crate::{
    types::{RefundsResponseRouterData, ResponseRouterData},
    utils::{
        get_unimplemented_payment_method_error_message, CardData, RouterData as OtherRouterData,
    },
};
pub struct JpmorganRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}

impl<T> From<(MinorUnit, T)> for JpmorganRouterData<T> {
    fn from((amount, item): (MinorUnit, T)) -> Self {
        Self {
            amount,
            router_data: item,
        }
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct JpmorganAuthUpdateRequest {
    pub grant_type: String,
    pub scope: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct JpmorganAuthUpdateResponse {
    pub access_token: Secret<String>,
    pub scope: String,
    pub token_type: String,
    pub expires_in: i64,
}

impl TryFrom<&RefreshTokenRouterData> for JpmorganAuthUpdateRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(_item: &RefreshTokenRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            grant_type: String::from("client_credentials"),
            scope: String::from("jpm:payments:sandbox"),
        })
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, JpmorganAuthUpdateResponse, T, AccessToken>>
    for RouterData<F, T, AccessToken>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, JpmorganAuthUpdateResponse, T, AccessToken>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(AccessToken {
                token: item.response.access_token,
                expires: item.response.expires_in,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganPaymentsRequest {
    capture_method: CapMethod,
    amount: MinorUnit,
    currency: common_enums::Currency,
    merchant: JpmorganMerchant,
    payment_method_type: JpmorganPaymentMethodType,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganCard {
    account_number: Secret<String>,
    expiry: Expiry,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganPaymentMethodType {
    card: JpmorganCard,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Expiry {
    month: Secret<i32>,
    year: Secret<i32>,
}

#[derive(Serialize, Debug, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganMerchantSoftware {
    company_name: Secret<String>,
    product_name: Secret<String>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganMerchant {
    merchant_software: JpmorganMerchantSoftware,
}

fn map_capture_method(
    capture_method: CaptureMethod,
) -> Result<CapMethod, error_stack::Report<errors::ConnectorError>> {
    match capture_method {
        CaptureMethod::Automatic => Ok(CapMethod::Now),
        CaptureMethod::Manual => Ok(CapMethod::Manual),
        CaptureMethod::Scheduled
        | CaptureMethod::ManualMultiple
        | CaptureMethod::SequentialAutomatic => {
            Err(errors::ConnectorError::NotImplemented("Capture Method".to_string()).into())
        }
    }
}

impl TryFrom<&JpmorganRouterData<&PaymentsAuthorizeRouterData>> for JpmorganPaymentsRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &JpmorganRouterData<&PaymentsAuthorizeRouterData>,
    ) -> Result<Self, Self::Error> {
        match item.router_data.request.payment_method_data.clone() {
            PaymentMethodData::Card(req_card) => {
                if item.router_data.is_three_ds() {
                    return Err(errors::ConnectorError::NotSupported {
                        message: "3DS payments".to_string(),
                        connector: "Jpmorgan",
                    }
                    .into());
                }

                let capture_method =
                    map_capture_method(item.router_data.request.capture_method.unwrap_or_default());

                let merchant_software = JpmorganMerchantSoftware {
                    company_name: String::from("JPMC").into(),
                    product_name: String::from("Hyperswitch").into(),
                };

                let merchant = JpmorganMerchant { merchant_software };

                let expiry: Expiry = Expiry {
                    month: Secret::new(
                        req_card
                            .card_exp_month
                            .peek()
                            .clone()
                            .parse::<i32>()
                            .change_context(errors::ConnectorError::RequestEncodingFailed)?,
                    ),
                    year: req_card.get_expiry_year_as_4_digit_i32()?,
                };

                let account_number = Secret::new(req_card.card_number.to_string());

                let card = JpmorganCard {
                    account_number,
                    expiry,
                };

                let payment_method_type = JpmorganPaymentMethodType { card };

                Ok(Self {
                    capture_method: capture_method?,
                    currency: item.router_data.request.currency,
                    amount: item.amount,
                    merchant,
                    payment_method_type,
                })
            }
            PaymentMethodData::CardDetailsForNetworkTransactionId(_)
            | PaymentMethodData::CardRedirect(_)
            | PaymentMethodData::Wallet(_)
            | PaymentMethodData::PayLater(_)
            | PaymentMethodData::BankRedirect(_)
            | PaymentMethodData::BankDebit(_)
            | PaymentMethodData::BankTransfer(_)
            | PaymentMethodData::Crypto(_)
            | PaymentMethodData::MandatePayment
            | PaymentMethodData::Reward
            | PaymentMethodData::RealTimePayment(_)
            | PaymentMethodData::MobilePayment(_)
            | PaymentMethodData::Upi(_)
            | PaymentMethodData::Voucher(_)
            | PaymentMethodData::GiftCard(_)
            | PaymentMethodData::OpenBanking(_)
            | PaymentMethodData::CardToken(_)
            | PaymentMethodData::NetworkToken(_) => Err(errors::ConnectorError::NotImplemented(
                get_unimplemented_payment_method_error_message("jpmorgan"),
            )
            .into()),
        }
    }
}

//JP Morgan uses access token only due to which we aren't reading the fields in this struct
#[derive(Debug)]
pub struct JpmorganAuthType {
    pub(super) _api_key: Secret<String>,
    pub(super) _key1: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for JpmorganAuthType {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
        match auth_type {
            ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
                _api_key: api_key.to_owned(),
                _key1: key1.to_owned(),
            }),
            _ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
        }
    }
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum JpmorganTransactionStatus {
    Success,
    Denied,
    Error,
}

#[derive(Default, Debug, Serialize, Deserialize, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub enum JpmorganTransactionState {
    Closed,
    Authorized,
    Voided,
    #[default]
    Pending,
    Declined,
    Error,
}

#[derive(Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganPaymentsResponse {
    transaction_id: String,
    request_id: String,
    transaction_state: JpmorganTransactionState,
    response_status: String,
    response_code: String,
    response_message: String,
    payment_method_type: PaymentMethodType,
    capture_method: Option<CapMethod>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Merchant {
    merchant_id: Option<String>,
    merchant_software: JpmorganMerchantSoftware,
    merchant_category_code: Option<String>,
}

#[derive(Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentMethodType {
    card: Option<Card>,
}

#[derive(Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Card {
    expiry: Option<ExpiryResponse>,
    card_type: Option<Secret<String>>,
    card_type_name: Option<Secret<String>>,
    masked_account_number: Option<Secret<String>>,
    card_type_indicators: Option<CardTypeIndicators>,
    network_response: Option<NetworkResponse>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkResponse {
    address_verification_result: Option<Secret<String>>,
    address_verification_result_code: Option<Secret<String>>,
    card_verification_result_code: Option<Secret<String>>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ExpiryResponse {
    month: Option<Secret<i32>>,
    year: Option<Secret<i32>>,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CardTypeIndicators {
    issuance_country_code: Option<Secret<String>>,
    is_durbin_regulated: Option<bool>,
    card_product_types: Secret<Vec<String>>,
}

pub fn attempt_status_from_transaction_state(
    transaction_state: JpmorganTransactionState,
) -> common_enums::AttemptStatus {
    match transaction_state {
        JpmorganTransactionState::Authorized => common_enums::AttemptStatus::Authorized,
        JpmorganTransactionState::Closed => common_enums::AttemptStatus::Charged,
        JpmorganTransactionState::Declined | JpmorganTransactionState::Error => {
            common_enums::AttemptStatus::Failure
        }
        JpmorganTransactionState::Pending => common_enums::AttemptStatus::Pending,
        JpmorganTransactionState::Voided => common_enums::AttemptStatus::Voided,
    }
}

impl<F, T> TryFrom<ResponseRouterData<F, JpmorganPaymentsResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;

    fn try_from(
        item: ResponseRouterData<F, JpmorganPaymentsResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let transaction_state = match item.response.transaction_state {
            JpmorganTransactionState::Closed => match item.response.capture_method {
                Some(CapMethod::Now) => JpmorganTransactionState::Closed,
                _ => JpmorganTransactionState::Authorized,
            },
            JpmorganTransactionState::Authorized => JpmorganTransactionState::Authorized,
            JpmorganTransactionState::Voided => JpmorganTransactionState::Voided,
            JpmorganTransactionState::Pending => JpmorganTransactionState::Pending,
            JpmorganTransactionState::Declined => JpmorganTransactionState::Declined,
            JpmorganTransactionState::Error => JpmorganTransactionState::Error,
        };
        let status = attempt_status_from_transaction_state(transaction_state);

        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.transaction_id.clone(),
                ),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.transaction_id.clone()),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganCaptureRequest {
    capture_method: Option<CapMethod>,
    amount: MinorUnit,
    currency: Option<common_enums::Currency>,
}

#[derive(Debug, Default, Copy, Serialize, Deserialize, Clone)]
#[serde(rename_all = "UPPERCASE")]
pub enum CapMethod {
    #[default]
    Now,
    Delayed,
    Manual,
}

impl TryFrom<&JpmorganRouterData<&PaymentsCaptureRouterData>> for JpmorganCaptureRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: &JpmorganRouterData<&PaymentsCaptureRouterData>,
    ) -> Result<Self, Self::Error> {
        let capture_method = Some(map_capture_method(
            item.router_data.request.capture_method.unwrap_or_default(),
        )?);
        Ok(Self {
            capture_method,
            amount: item.amount,
            currency: Some(item.router_data.request.currency),
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganCaptureResponse {
    pub transaction_id: String,
    pub request_id: String,
    pub transaction_state: JpmorganTransactionState,
    pub response_status: JpmorganTransactionStatus,
    pub response_code: String,
    pub response_message: String,
    pub payment_method_type: PaymentMethodTypeCapRes,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct PaymentMethodTypeCapRes {
    pub card: Option<CardCapRes>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardCapRes {
    pub card_type: Option<Secret<String>>,
    pub card_type_name: Option<Secret<String>>,
    unmasked_account_number: Option<Secret<String>>,
}

impl<F, T> TryFrom<ResponseRouterData<F, JpmorganCaptureResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, JpmorganCaptureResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let status = attempt_status_from_transaction_state(item.response.transaction_state);
        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.transaction_id.clone(),
                ),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.transaction_id.clone()),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganPSyncResponse {
    transaction_id: String,
    transaction_state: JpmorganTransactionState,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "UPPERCASE")]
pub enum JpmorganResponseStatus {
    Success,
    Denied,
    Error,
}

impl<F, PaymentsSyncData>
    TryFrom<ResponseRouterData<F, JpmorganPSyncResponse, PaymentsSyncData, PaymentsResponseData>>
    for RouterData<F, PaymentsSyncData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<F, JpmorganPSyncResponse, PaymentsSyncData, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let status = attempt_status_from_transaction_state(item.response.transaction_state);
        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.transaction_id.clone(),
                ),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.transaction_id.clone()),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TransactionData {
    payment_type: Option<Secret<String>>,
    status_code: Secret<i32>,
    txn_secret: Option<Secret<String>>,
    tid: Option<Secret<i64>>,
    test_mode: Option<Secret<i8>>,
    status: Option<JpmorganTransactionStatus>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganRefundRequest {
    pub merchant: MerchantRefundReq,
    pub amount: MinorUnit,
    pub currency: common_enums::Currency,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MerchantRefundReq {
    pub merchant_software: JpmorganMerchantSoftware,
}

impl<F> TryFrom<&JpmorganRouterData<&RefundsRouterData<F>>> for JpmorganRefundRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: &JpmorganRouterData<&RefundsRouterData<F>>) -> Result<Self, Self::Error> {
        let merchant_software = JpmorganMerchantSoftware {
            company_name: String::from("JPMC").into(),
            product_name: String::from("Hyperswitch").into(),
        };
        let merchant = MerchantRefundReq { merchant_software };
        let amount = item.amount;
        let currency = item.router_data.request.currency;

        Ok(Self {
            merchant,
            amount,
            currency,
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganRefundResponse {
    pub transaction_id: Option<String>,
    pub request_id: String,
    pub transaction_state: JpmorganTransactionState,
    pub amount: MinorUnit,
    pub currency: common_enums::Currency,
    pub response_status: JpmorganResponseStatus,
    pub response_code: String,
    pub response_message: String,
    pub transaction_reference_id: Option<String>,
    pub remaining_refundable_amount: Option<i64>,
}

#[derive(Debug, Serialize, Default, Deserialize, Clone)]
pub enum RefundStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
}

impl From<RefundStatus> for common_enums::RefundStatus {
    fn from(item: RefundStatus) -> Self {
        match item {
            RefundStatus::Succeeded => Self::Success,
            RefundStatus::Failed => Self::Failure,
            RefundStatus::Processing => Self::Pending,
        }
    }
}

impl From<(JpmorganResponseStatus, JpmorganTransactionState)> for RefundStatus {
    fn from(
        (response_status, transaction_state): (JpmorganResponseStatus, JpmorganTransactionState),
    ) -> Self {
        match response_status {
            JpmorganResponseStatus::Success => match transaction_state {
                JpmorganTransactionState::Voided | JpmorganTransactionState::Closed => {
                    Self::Succeeded
                }
                JpmorganTransactionState::Declined | JpmorganTransactionState::Error => {
                    Self::Failed
                }
                JpmorganTransactionState::Pending | JpmorganTransactionState::Authorized => {
                    Self::Processing
                }
            },
            JpmorganResponseStatus::Denied | JpmorganResponseStatus::Error => Self::Failed,
        }
    }
}

impl TryFrom<RefundsResponseRouterData<Execute, JpmorganRefundResponse>>
    for RefundsRouterData<Execute>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<Execute, JpmorganRefundResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item
                    .response
                    .transaction_id
                    .clone()
                    .ok_or(errors::ConnectorError::ResponseHandlingFailed)?,
                refund_status: RefundStatus::from((
                    item.response.response_status,
                    item.response.transaction_state,
                ))
                .into(),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganRefundSyncResponse {
    transaction_id: String,
    request_id: String,
    transaction_state: JpmorganTransactionState,
    amount: MinorUnit,
    currency: common_enums::Currency,
    response_status: JpmorganResponseStatus,
    response_code: String,
}

impl TryFrom<RefundsResponseRouterData<RSync, JpmorganRefundSyncResponse>>
    for RefundsRouterData<RSync>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: RefundsResponseRouterData<RSync, JpmorganRefundSyncResponse>,
    ) -> Result<Self, Self::Error> {
        Ok(Self {
            response: Ok(RefundsResponseData {
                connector_refund_id: item.response.transaction_id.clone(),
                refund_status: RefundStatus::from((
                    item.response.response_status,
                    item.response.transaction_state,
                ))
                .into(),
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ReversalReason {
    NoResponse,
    LateResponse,
    UnableToDeliver,
    CardDeclined,
    MacNotVerified,
    MacSyncError,
    ZekSyncError,
    SystemMalfunction,
    SuspectedFraud,
}

impl FromStr for ReversalReason {
    type Err = error_stack::Report<errors::ConnectorError>;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_uppercase().as_str() {
            "NO_RESPONSE" => Ok(Self::NoResponse),
            "LATE_RESPONSE" => Ok(Self::LateResponse),
            "UNABLE_TO_DELIVER" => Ok(Self::UnableToDeliver),
            "CARD_DECLINED" => Ok(Self::CardDeclined),
            "MAC_NOT_VERIFIED" => Ok(Self::MacNotVerified),
            "MAC_SYNC_ERROR" => Ok(Self::MacSyncError),
            "ZEK_SYNC_ERROR" => Ok(Self::ZekSyncError),
            "SYSTEM_MALFUNCTION" => Ok(Self::SystemMalfunction),
            "SUSPECTED_FRAUD" => Ok(Self::SuspectedFraud),
            _ => Err(report!(errors::ConnectorError::InvalidDataFormat {
                field_name: "cancellation_reason",
            })),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganCancelRequest {
    pub amount: Option<i64>,
    pub is_void: Option<bool>,
    pub reversal_reason: Option<ReversalReason>,
}

impl TryFrom<JpmorganRouterData<&PaymentsCancelRouterData>> for JpmorganCancelRequest {
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(item: JpmorganRouterData<&PaymentsCancelRouterData>) -> Result<Self, Self::Error> {
        Ok(Self {
            amount: item.router_data.request.amount,
            is_void: Some(true),
            reversal_reason: item
                .router_data
                .request
                .cancellation_reason
                .as_ref()
                .map(|reason| ReversalReason::from_str(reason))
                .transpose()?,
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganCancelResponse {
    transaction_id: String,
    request_id: String,
    response_status: JpmorganResponseStatus,
    response_code: String,
    response_message: String,
    payment_method_type: JpmorganPaymentMethodTypeCancelResponse,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganPaymentMethodTypeCancelResponse {
    pub card: CardCancelResponse,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CardCancelResponse {
    pub card_type: Secret<String>,
    pub card_type_name: Secret<String>,
}

impl<F>
    TryFrom<ResponseRouterData<F, JpmorganCancelResponse, PaymentsCancelData, PaymentsResponseData>>
    for RouterData<F, PaymentsCancelData, PaymentsResponseData>
{
    type Error = error_stack::Report<errors::ConnectorError>;
    fn try_from(
        item: ResponseRouterData<
            F,
            JpmorganCancelResponse,
            PaymentsCancelData,
            PaymentsResponseData,
        >,
    ) -> Result<Self, Self::Error> {
        let status = match item.response.response_status {
            JpmorganResponseStatus::Success => common_enums::AttemptStatus::Voided,
            JpmorganResponseStatus::Denied | JpmorganResponseStatus::Error => {
                common_enums::AttemptStatus::Failure
            }
        };
        Ok(Self {
            status,
            response: Ok(PaymentsResponseData::TransactionResponse {
                resource_id: ResponseId::ConnectorTransactionId(
                    item.response.transaction_id.clone(),
                ),
                redirection_data: Box::new(None),
                mandate_reference: Box::new(None),
                connector_metadata: None,
                network_txn_id: None,
                connector_response_reference_id: Some(item.response.transaction_id.clone()),
                incremental_authorization_allowed: None,
                charges: None,
            }),
            ..item.data
        })
    }
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganValidationErrors {
    pub code: Option<String>,
    pub message: Option<String>,
    pub entity: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganErrorInformation {
    pub code: Option<String>,
    pub message: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JpmorganErrorResponse {
    pub response_status: JpmorganTransactionStatus,
    pub response_code: String,
    pub response_message: Option<String>,
}


pub struct EliminationAnalyserConfig {
    pub bucket_size: Option<u64>,
    pub bucket_leak_interval_in_secs: Option<u64>,
}


pub struct KafkaRefund<'a> {
    pub refund_id: &'a id_type::GlobalRefundId,
    pub merchant_reference_id: &'a id_type::RefundReferenceId,
    pub payment_id: &'a id_type::GlobalPaymentId,
    pub merchant_id: &'a id_type::MerchantId,
    pub connector_transaction_id: &'a types::ConnectorTransactionId,
    pub connector: &'a String,
    pub connector_refund_id: Option<&'a types::ConnectorTransactionId>,
    pub external_reference_id: Option<&'a String>,
    pub refund_type: &'a storage_enums::RefundType,
    pub total_amount: &'a MinorUnit,
    pub currency: &'a storage_enums::Currency,
    pub refund_amount: &'a MinorUnit,
    pub refund_status: &'a storage_enums::RefundStatus,
    pub sent_to_gateway: &'a bool,
    pub refund_error_message: Option<&'a String>,
    pub refund_arn: Option<&'a String>,
    #[serde(default, with = "time::serde::timestamp")]
    pub created_at: OffsetDateTime,
    #[serde(default, with = "time::serde::timestamp")]
    pub modified_at: OffsetDateTime,
    pub description: Option<&'a String>,
    pub attempt_id: &'a id_type::GlobalAttemptId,
    pub refund_reason: Option<&'a String>,
    pub refund_error_code: Option<&'a String>,
    pub profile_id: Option<&'a id_type::ProfileId>,
    pub organization_id: &'a id_type::OrganizationId,

    pub metadata: Option<&'a pii::SecretSerdeValue>,
    pub updated_by: &'a String,
    pub merchant_connector_id: Option<&'a id_type::MerchantConnectorAccountId>,
    pub charges: Option<&'a ChargeRefunds>,
    pub connector_refund_data: Option<&'a String>,
    pub connector_transaction_data: Option<&'a String>,
    pub split_refunds: Option<&'a common_types::refunds::SplitRefund>,
    pub unified_code: Option<&'a String>,
    pub unified_message: Option<&'a String>,
    pub processor_refund_data: Option<&'a String>,
    pub processor_transaction_data: Option<&'a String>,
}


pub struct CustomerBankAccountResponse {
    pub id: Secret<String>,
}


pub fn server(state: AppState) -> Scope {
            web::scope("/analytics").app_data(web::Data::new(state))
        }


File: crates/router_derive/src/lib.rs
 Public functions: 16
 Public structs: 3

//! Utility macros for the `router` crate.
#![warn(missing_docs)]
use syn::parse_macro_input;

use crate::macros::diesel::DieselEnumMeta;
mod macros;

/// Uses the [`Debug`][Debug] implementation of a type to derive its [`Display`][Display]
/// implementation.
///
/// Causes a compilation error if the type doesn't implement the [`Debug`][Debug] trait.
///
/// [Debug]: ::core::fmt::Debug
/// [Display]: ::core::fmt::Display
///
/// # Example
///
/// ```
/// use router_derive::DebugAsDisplay;
///
/// #[derive(Debug, DebugAsDisplay)]
/// struct Point {
///     x: f32,
///     y: f32,
/// }
///
/// #[derive(Debug, DebugAsDisplay)]
/// enum Color {
///     Red,
///     Green,
///     Blue,
/// }
/// ```
#[proc_macro_derive(DebugAsDisplay)]
pub fn debug_as_display_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);

    let tokens =
        macros::debug_as_display_inner(&ast).unwrap_or_else(|error| error.to_compile_error());
    tokens.into()
}

/// Derives the boilerplate code required for using an enum with `diesel` and a PostgreSQL database.
/// The enum is required to implement (or derive) the [`ToString`][ToString] and the
/// [`FromStr`][FromStr] traits for this derive macro to be used.
///
/// Works in tandem with the [`diesel_enum`][diesel_enum] attribute macro to achieve the desired
/// results.
///
/// [diesel_enum]: macro@crate::diesel_enum
/// [FromStr]: ::core::str::FromStr
/// [ToString]: ::std::string::ToString
///
/// # Example
///
/// ```
/// use router_derive::diesel_enum;
///
/// // Deriving `FromStr` and `ToString` using the `strum` crate, you can also implement it
/// // yourself if required.
/// #[derive(strum::Display, strum::EnumString)]
/// #[derive(Debug)]
/// #[diesel_enum(storage_type = "db_enum")]
/// enum Color {
///     Red,
///     Green,
///     Blue,
/// }
/// ```
#[proc_macro_derive(DieselEnum, attributes(storage_type))]
pub fn diesel_enum_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
    let tokens =
        macros::diesel_enum_derive_inner(&ast).unwrap_or_else(|error| error.to_compile_error());
    tokens.into()
}

/// Similar to [`DieselEnum`] but uses text when storing in the database, this is to avoid
/// making changes to the database when the enum variants are added or modified
///
/// # Example
/// [DieselEnum]: macro@crate::diesel_enum
///
/// ```
/// use router_derive::{diesel_enum};
///
/// // Deriving `FromStr` and `ToString` using the `strum` crate, you can also implement it
/// // yourself if required.
/// #[derive(strum::Display, strum::EnumString)]
/// #[derive(Debug)]
/// #[diesel_enum(storage_type = "text")]
/// enum Color {
///     Red,
///     Green,
///     Blue,
/// }
/// ```
#[proc_macro_derive(DieselEnumText)]
pub fn diesel_enum_derive_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);
    let tokens = macros::diesel_enum_text_derive_inner(&ast)
        .unwrap_or_else(|error| error.to_compile_error());
    tokens.into()
}

/// Derives the boilerplate code required for using an enum with `diesel` and a PostgreSQL database.
///
/// Storage Type can either be "text" or "db_enum"
/// Choosing text will store the enum as text in the database, whereas db_enum will map it to the
/// corresponding database enum
///
/// Works in tandem with the [`DieselEnum`][DieselEnum] derive macro to achieve the desired results.
/// The enum is required to implement (or derive) the [`ToString`][ToString] and the
/// [`FromStr`][FromStr] traits for the [`DieselEnum`][DieselEnum] derive macro to be used.
///
/// [DieselEnum]: crate::DieselEnum
/// [FromStr]: ::core::str::FromStr
/// [ToString]: ::std::string::ToString
///
/// # Example
///
/// ```
/// use router_derive::{diesel_enum};
///
/// // Deriving `FromStr` and `ToString` using the `strum` crate, you can also implement it
/// // yourself if required. (Required by the DieselEnum derive macro.)
/// #[derive(strum::Display, strum::EnumString)]
/// #[derive(Debug)]
/// #[diesel_enum(storage_type = "text")]
/// enum Color {
///     Red,
///     Green,
///     Blue,
/// }
/// ```
#[proc_macro_attribute]
pub fn diesel_enum(
    args: proc_macro::TokenStream,
    item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
    let args_parsed = parse_macro_input!(args as DieselEnumMeta);
    let item = syn::parse_macro_input!(item as syn::ItemEnum);

    macros::diesel::diesel_enum_attribute_macro(args_parsed, &item)
        .unwrap_or_else(|error| error.to_compile_error())
        .into()
}

/// A derive macro which generates the setter functions for any struct with fields
/// # Example
/// ```
/// use router_derive::Setter;
///
/// #[derive(Setter)]
/// struct Test {
///     test:u32
/// }
/// ```
/// The above Example will expand to
/// ```rust, ignore
/// impl Test {
///     fn set_test(&mut self, val: u32) -> &mut Self {
///         self.test = val;
///         self
///     }
/// }
/// ```
///
/// # Panics
///
/// Panics if a struct without named fields is provided as input to the macro
// FIXME: Remove allowed warnings, raise compile errors in a better manner instead of panicking
#[allow(clippy::panic, clippy::unwrap_used)]
#[proc_macro_derive(Setter, attributes(auth_based))]
pub fn setter(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    let ident = &input.ident;
    // All the fields in the parent struct
    let fields = if let syn::Data::Struct(syn::DataStruct {
        fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
        ..
    }) = input.data
    {
        named
    } else {
        // FIXME: Use `compile_error!()` instead
        panic!("You can't use this proc-macro on structs without fields");
    };

    // Methods in the build struct like if the struct is
    // Struct i {n: u32}
    // this will be
    // pub fn set_n(&mut self,n: u32)
    let build_methods = fields.iter().map(|f| {
        let name = f.ident.as_ref().unwrap();
        let method_name = format!("set_{name}");
        let method_ident = syn::Ident::new(&method_name, name.span());
        let ty = &f.ty;
        if check_if_auth_based_attr_is_present(f, "auth_based") {
            quote::quote! {
                pub fn #method_ident(&mut self, val:#ty, is_merchant_flow: bool)->&mut Self{
                    if is_merchant_flow {
                        self.#name = val;
                    }
                    self
                }
            }
        } else {
            quote::quote! {
                pub fn #method_ident(&mut self, val:#ty)->&mut Self{
                    self.#name = val;
                    self
                }
            }
        }
    });
    let output = quote::quote! {
    #[automatically_derived]
    impl #ident {
            #(#build_methods)*
        }

    };
    output.into()
}

#[inline]
fn check_if_auth_based_attr_is_present(f: &syn::Field, ident: &str) -> bool {
    for i in f.attrs.iter() {
        if i.path().is_ident(ident) {
            return true;
        }
    }
    false
}

/// Derives the [`Serialize`][Serialize] implementation for error responses that are returned by
/// the API server.
///
/// This macro can be only used with enums. In addition to deriving [`Serialize`][Serialize], this
/// macro provides three methods: `error_type()`, `error_code()` and `error_message()`. Each enum
/// variant must have three required fields:
///
/// - `error_type`: This must be an enum variant which is returned by the `error_type()` method.
/// - `code`: A string error code, returned by the `error_code()` method.
/// - `message`: A string error message, returned by the `error_message()` method. The message
///   provided will directly be passed to `format!()`.
///
/// The return type of the `error_type()` method is provided by the `error_type_enum` field
/// annotated to the entire enum. Thus, all enum variants provided to the `error_type` field must
/// be variants of the enum provided to `error_type_enum` field. In addition, the enum passed to
/// the `error_type_enum` field must implement [`Serialize`][Serialize].
///
/// **NOTE:** This macro does not implement the [`Display`][Display] trait.
///
/// # Example
///
/// ```
/// use router_derive::ApiError;
///
/// #[derive(Clone, Debug, serde::Serialize)]
/// enum ErrorType {
///     StartupError,
///     InternalError,
///     SerdeError,
/// }
///
/// #[derive(Debug, ApiError)]
/// #[error(error_type_enum = ErrorType)]
/// enum MyError {
///     #[error(error_type = ErrorType::StartupError, code = "E001", message = "Failed to read configuration")]
///     ConfigurationError,
///     #[error(error_type = ErrorType::InternalError, code = "E002", message = "A database error occurred")]
///     DatabaseError,
///     #[error(error_type = ErrorType::SerdeError, code = "E003", message = "Failed to deserialize object")]
///     DeserializationError,
///     #[error(error_type = ErrorType::SerdeError, code = "E004", message = "Failed to serialize object")]
///     SerializationError,
/// }
///
/// impl ::std::fmt::Display for MyError {
///     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> {
///         f.write_str(&self.error_message())
///     }
/// }
/// ```
///
/// # The Generated `Serialize` Implementation
///
/// - For a simple enum variant with no fields, the generated [`Serialize`][Serialize]
///   implementation has only three fields, `type`, `code` and `message`:
///
/// ```
/// # use router_derive::ApiError;
/// # #[derive(Clone, Debug, serde::Serialize)]
/// # enum ErrorType {
/// #     StartupError,
/// # }
/// #[derive(Debug, ApiError)]
/// #[error(error_type_enum = ErrorType)]
/// enum MyError {
///     #[error(error_type = ErrorType::StartupError, code = "E001", message = "Failed to read configuration")]
///     ConfigurationError,
///     // ...
/// }
/// # impl ::std::fmt::Display for MyError {
/// #     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> {
/// #         f.write_str(&self.error_message())
/// #     }
/// # }
///
/// let json = serde_json::json!({
///     "type": "StartupError",
///     "code": "E001",
///     "message": "Failed to read configuration"
/// });
/// assert_eq!(serde_json::to_value(MyError::ConfigurationError).unwrap(), json);
/// ```
///
/// - For an enum variant with named fields, the generated [`Serialize`][Serialize] implementation
///   includes three mandatory fields, `type`, `code` and `message`, and any other fields not
///   included in the message:
///
/// ```
/// # use router_derive::ApiError;
/// # #[derive(Clone, Debug, serde::Serialize)]
/// # enum ErrorType {
/// #     StartupError,
/// # }
/// #[derive(Debug, ApiError)]
/// #[error(error_type_enum = ErrorType)]
/// enum MyError {
///     #[error(error_type = ErrorType::StartupError, code = "E001", message = "Failed to read configuration file: {file_path}")]
///     ConfigurationError { file_path: String, reason: String },
///     // ...
/// }
/// # impl ::std::fmt::Display for MyError {
/// #     fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::result::Result<(), ::core::fmt::Error> {
/// #         f.write_str(&self.error_message())
/// #     }
/// # }
///
/// let json = serde_json::json!({
///     "type": "StartupError",
///     "code": "E001",
///     "message": "Failed to read configuration file: config.toml",
///     "reason": "File not found"
/// });
/// let error = MyError::ConfigurationError{
///     file_path: "config.toml".to_string(),
///     reason: "File not found".to_string(),
/// };
/// assert_eq!(serde_json::to_value(error).unwrap(), json);
/// ```
///
/// [Serialize]: https://docs.rs/serde/latest/serde/trait.Serialize.html
/// [Display]: ::core::fmt::Display
#[proc_macro_derive(ApiError, attributes(error))]
pub fn api_error_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let ast = syn::parse_macro_input!(input as syn::DeriveInput);

    let tokens =
        macros::api_error_derive_inner(&ast).unwrap_or_else(|error| error.to_compile_error());
    tokens.into()
}

/// Derives the `core::payments::Operation` trait on a type with a default base
/// implementation.
///
/// ## Usage
/// On deriving, the conversion functions to be implemented need to be specified in an helper
/// attribute `#[operation(..)]`. To derive all conversion functions, use `#[operation(all)]`. To
/// derive specific conversion functions, pass the required identifiers to the attribute.
/// `#[operation(validate_request, get_tracker)]`. Available conversions are listed below :-
///
/// - validate_request
/// - get_tracker
/// - domain
/// - update_tracker
///
/// ## Example
/// ```rust, ignore
/// use router_derive::Operation;
///
/// #[derive(Operation)]
/// #[operation(all)]
/// struct Point {
///     x: u64,
///     y: u64
/// }
///
/// // The above will expand to this
/// const _: () = {
///     use crate::core::errors::RouterResult;
///     use crate::core::payments::{GetTracker, PaymentData, UpdateTracker, ValidateRequest};
///     impl crate::core::payments::Operation for Point {
///         fn to_validate_request(&self) -> RouterResult<&dyn ValidateRequest> {
///             Ok(self)
///         }
///         fn to_get_tracker(&self) -> RouterResult<&dyn GetTracker<PaymentData>> {
///             Ok(self)
///         }
///         fn to_domain(&self) -> RouterResult<&dyn Domain> {
///             Ok(self)
///         }
///         fn to_update_tracker(&self) -> RouterResult<&dyn UpdateTracker<PaymentData>> {
///             Ok(self)
///         }
///     }
///     impl crate::core::payments::Operation for &Point {
///         fn to_validate_request(&self) -> RouterResult<&dyn ValidateRequest> {
///             Ok(*self)
///         }
///         fn to_get_tracker(&self) -> RouterResult<&dyn GetTracker<PaymentData>> {
///             Ok(*self)
///         }
///         fn to_domain(&self) -> RouterResult<&dyn Domain> {
///             Ok(*self)
///         }
///         fn to_update_tracker(&self) -> RouterResult<&dyn UpdateTracker<PaymentData>> {
///             Ok(*self)
///         }
///     }
/// };
///
/// #[derive(Operation)]
/// #[operation(validate_request, get_tracker)]
/// struct Point3 {
///     x: u64,
///     y: u64,
///     z: u64
/// }
///
/// // The above will expand to this
/// const _: () = {
///     use crate::core::errors::RouterResult;
///     use crate::core::payments::{GetTracker, PaymentData, UpdateTracker, ValidateRequest};
///     impl crate::core::payments::Operation for Point3 {
///         fn to_validate_request(&self) -> RouterResult<&dyn ValidateRequest> {
///             Ok(self)
///         }
///         fn to_get_tracker(&self) -> RouterResult<&dyn GetTracker<PaymentData>> {
///             Ok(self)
///         }
///     }
///     impl crate::core::payments::Operation for &Point3 {
///         fn to_validate_request(&self) -> RouterResult<&dyn ValidateRequest> {
///             Ok(*self)
///         }
///         fn to_get_tracker(&self) -> RouterResult<&dyn GetTracker<PaymentData>> {
///             Ok(*self)
///         }
///     }
/// };
///
/// ```
///
/// The `const _: () = {}` allows us to import stuff with `use` without affecting the module
/// imports, since use statements are not allowed inside of impl blocks. This technique is
/// used by `diesel`.
#[proc_macro_derive(PaymentOperation, attributes(operation))]
pub fn operation_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);
    macros::operation::operation_derive_inner(input)
        .unwrap_or_else(|err| err.to_compile_error().into())
}

/// Generates different schemas with the ability to mark few fields as mandatory for certain schema
/// Usage
/// ```
/// use router_derive::PolymorphicSchema;
///
/// #[derive(PolymorphicSchema)]
/// #[generate_schemas(PaymentsCreateRequest, PaymentsConfirmRequest)]
/// struct PaymentsRequest {
///     #[mandatory_in(PaymentsCreateRequest = u64)]
///     amount: Option<u64>,
///     #[mandatory_in(PaymentsCreateRequest = String)]
///     currency: Option<String>,
///     payment_method: String,
/// }
/// ```
///
/// This will create two structs `PaymentsCreateRequest` and `PaymentsConfirmRequest` as follows
/// It will retain all the other attributes that are used in the original struct, and only consume
/// the #[mandatory_in] attribute to generate schemas
///
/// ```
/// #[derive(utoipa::ToSchema)]
/// struct PaymentsCreateRequest {
///     #[schema(required = true)]
///     amount: Option<u64>,
///
///     #[schema(required = true)]
///     currency: Option<String>,
///
///     payment_method: String,
/// }
///
/// #[derive(utoipa::ToSchema)]
/// struct PaymentsConfirmRequest {
///     amount: Option<u64>,
///     currency: Option<String>,
///     payment_method: String,
/// }
/// ```
#[proc_macro_derive(
    PolymorphicSchema,
    attributes(mandatory_in, generate_schemas, remove_in)
)]
pub fn polymorphic_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);

    macros::polymorphic_macro_derive_inner(input)
        .unwrap_or_else(|error| error.into_compile_error())
        .into()
}

/// Implements the `Validate` trait to check if the config variable is present
/// Usage
/// ```
/// use router_derive::ConfigValidate;
///
/// #[derive(ConfigValidate)]
/// struct ConnectorParams {
///     base_url: String,
/// }
///
/// enum ApplicationError {
///     InvalidConfigurationValueError(String),
/// }
///
/// #[derive(ConfigValidate)]
/// struct Connectors {
///     pub stripe: ConnectorParams,
///     pub checkout: ConnectorParams
/// }
/// ```
///
/// This will call the `validate()` function for all the fields in the struct
///
/// ```rust, ignore
/// impl Connectors {
///      fn validate(&self) -> Result<(), ApplicationError> {
///         self.stripe.validate()?;
///         self.checkout.validate()?;
///      }
/// }
/// ```
#[proc_macro_derive(ConfigValidate)]
pub fn validate_config(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);

    macros::misc::validate_config(input)
        .unwrap_or_else(|error| error.into_compile_error())
        .into()
}

/// Generates the function to get the value out of enum variant
/// Usage
/// ```
/// use router_derive::TryGetEnumVariant;
///
/// impl std::fmt::Display for RedisError {
///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
///         match self {
///             Self::UnknownResult => write!(f, "Unknown result")
///         }
///     }
/// }
///
/// impl std::error::Error for RedisError {}
///
/// #[derive(Debug)]
/// enum RedisError {
///     UnknownResult
/// }
///
/// #[derive(TryGetEnumVariant)]
/// #[error(RedisError::UnknownResult)]
/// enum RedisResult {
///     Set(String),
///     Get(i32)
/// }
/// ```
///
/// This will generate the function to get `String` and `i32` out of the variants
///
/// ```rust, ignore
/// impl RedisResult {
///     fn try_into_get(&self)-> Result<i32, RedisError> {
///         match self {
///             Self::Get(a) => Ok(a),
///             _=>Err(RedisError::UnknownResult)
///         }
///     }
///
///     fn try_into_set(&self)-> Result<String, RedisError> {
///         match self {
///             Self::Set(a) => Ok(a),
///             _=> Err(RedisError::UnknownResult)
///         }
///     }
/// }
/// ```
#[proc_macro_derive(TryGetEnumVariant, attributes(error))]
pub fn try_get_enum_variant(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);

    macros::try_get_enum::try_get_enum_variant(input)
        .unwrap_or_else(|error| error.into_compile_error())
        .into()
}

/// Uses the [`Serialize`] implementation of a type to derive a function implementation
/// for converting nested keys structure into a HashMap of key, value where key is in
/// the flattened form.
///
/// Example
///
/// ```
/// #[derive(Default, Serialize, FlatStruct)]
/// pub struct User {
///     name: String,
///     address: Address,
///     email: String,
/// }
///
/// #[derive(Default, Serialize)]
/// pub struct Address {
///     line1: String,
///     line2: String,
///     zip: String,
/// }
///
/// let user = User::default();
/// let flat_struct_map = user.flat_struct();
///
/// [
///     ("name", "Test"),
///     ("address.line1", "1397"),
///     ("address.line2", "Some street"),
///     ("address.zip", "941222"),
///     ("email", "test@example.com"),
/// ]
/// ```
#[proc_macro_derive(FlatStruct)]
pub fn flat_struct_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = parse_macro_input!(input as syn::DeriveInput);
    let name = &input.ident;

    let expanded = quote::quote! {
        impl #name {
            pub fn flat_struct(&self) -> std::collections::HashMap<String, String> {
                use serde_json::Value;
                use std::collections::HashMap;

                fn flatten_value(
                    value: &Value,
                    prefix: &str,
                    result: &mut HashMap<String, String>
                ) {
                    match value {
                        Value::Object(map) => {
                            for (key, val) in map {
                                let new_key = if prefix.is_empty() {
                                    key.to_string()
                                } else {
                                    format!("{}.{}", prefix, key)
                                };
                                flatten_value(val, &new_key, result);
                            }
                        }
                        Value::String(s) => {
                            result.insert(prefix.to_string(), s.clone());
                        }
                        Value::Number(n) => {
                            result.insert(prefix.to_string(), n.to_string());
                        }
                        Value::Bool(b) => {
                            result.insert(prefix.to_string(), b.to_string());
                        }
                        _ => {}
                    }
                }

                let mut result = HashMap::new();
                let value = serde_json::to_value(self).unwrap();
                flatten_value(&value, "", &mut result);
                result
            }
        }
    };

    proc_macro::TokenStream::from(expanded)
}

/// Generates the permissions enum and implematations for the permissions
///
/// **NOTE:** You have to make sure that all the identifiers used
/// in the macro input are present in the respective enums as well.
///
/// ## Usage
/// ```
/// use router_derive::generate_permissions;
///
/// enum Scope {
///     Read,
///     Write,
/// }
///
/// enum EntityType {
///    Profile,
///    Merchant,
///    Org,
/// }
///
/// enum Resource {
///    Payments,
///    Refunds,
/// }
///
/// generate_permissions! {
///     permissions: [
///         Payments: {
///             scopes: [Read, Write],
///             entities: [Profile, Merchant, Org]
///         },
///         Refunds: {
///             scopes: [Read],
///             entities: [Profile, Org]
///         }
///     ]
/// }
/// ```
/// This will generate the following enum.
/// ```
/// enum Permission {
///    ProfilePaymentsRead,
///    ProfilePaymentsWrite,
///    MerchantPaymentsRead,
///    MerchantPaymentsWrite,
///    OrgPaymentsRead,
///    OrgPaymentsWrite,
///    ProfileRefundsRead,
///    OrgRefundsRead,
/// ```
#[proc_macro]
pub fn generate_permissions(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    macros::generate_permissions_inner(input)
}

/// Generates the ToEncryptable trait for a type
///
/// This macro generates the temporary structs which has the fields that needs to be encrypted
///
/// fn to_encryptable: Convert the temp struct to a hashmap that can be sent over the network
/// fn from_encryptable: Convert the hashmap back to temp struct
#[proc_macro_derive(ToEncryption, attributes(encrypt))]
pub fn derive_to_encryption_attr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);

    macros::derive_to_encryption(input)
        .unwrap_or_else(|err| err.into_compile_error())
        .into()
}

/// Derives validation functionality for structs with string-based fields that have
/// schema attributes specifying constraints like minimum and maximum lengths.
///
/// This macro generates a `validate()` method that checks if string based fields
/// meet the length requirements specified in their schema attributes.
///
/// ## Supported Types
///   - Option<T> or T: where T: String or Url
///
/// ## Supported Schema Attributes
///
/// - `min_length`: Specifies the minimum allowed character length
/// - `max_length`: Specifies the maximum allowed character length
///
/// ## Example
///
/// ```
/// use utoipa::ToSchema;
/// use router_derive::ValidateSchema;
/// use url::Url;
///
/// #[derive(Default, ToSchema, ValidateSchema)]
/// pub struct PaymentRequest {
///     #[schema(min_length = 10, max_length = 255)]
///     pub description: String,
///
///     #[schema(example = "https://example.com/return", max_length = 255)]
///     pub return_url: Option<Url>,
///
///     // Field without constraints
///     pub amount: u64,
/// }
///
/// let payment = PaymentRequest {
///     description: "Too short".to_string(),
///     return_url: Some(Url::parse("https://very-long-domain.com/callback").unwrap()),
///     amount: 1000,
/// };
///
/// let validation_result = payment.validate();
/// assert!(validation_result.is_err());
/// assert_eq!(
///     validation_result.unwrap_err(),
///     "description must be at least 10 characters long. Received 9 characters"
/// );
/// ```
///
/// ## Notes
/// - For `Option` fields, validation is only performed when the value is `Some`
/// - Fields without schema attributes or with unsupported types are ignored
/// - The validation stops on the first error encountered
/// - The generated `validate()` method returns `Ok(())` if all validations pass, or
///   `Err(String)` with an error message if any validations fail.
#[proc_macro_derive(ValidateSchema, attributes(schema))]
pub fn validate_schema(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input = syn::parse_macro_input!(input as syn::DeriveInput);

    macros::validate_schema_derive(input)
        .unwrap_or_else(|error| error.into_compile_error())
        .into()
}


pub struct BillingDetails {
    country_code: Option<CountryAlpha2>,
    address_lines: Option<Vec<Secret<String>>>,
    family_name: Option<Secret<String>>,
    given_name: Option<Secret<String>>,
    postal_code: Option<Secret<String>>,
}


pub struct MerchantConnectorCreate {
    /// Type of the Connector for the financial use case. Could range from Payments to Accounting to Banking.
    #[schema(value_type = ConnectorType, example = "payment_processor")]
    pub connector_type: api_enums::ConnectorType,
    /// Name of the Connector
    #[schema(value_type = Connector, example = "stripe")]
    pub connector_name: api_enums::Connector,
    /// This is an unique label you can generate and pass in order to identify this connector account on your Hyperswitch dashboard and reports. Eg: if your profile label is `default`, connector label can be `stripe_default`
    #[schema(example = "stripe_US_travel")]
    pub connector_label: Option<String>,

    /// Identifier for the profile, if not provided default will be chosen from merchant account
    #[schema(max_length = 64, value_type = Option<String>)]
    pub profile_id: Option<id_type::ProfileId>,

    /// An object containing the required details/credentials for a Connector account.
    #[schema(value_type = Option<MerchantConnectorDetails>,example = json!({ "auth_type": "HeaderKey","api_key": "Basic MyVerySecretApiKey" }))]
    pub connector_account_details: Option<pii::SecretSerdeValue>,

    /// An object containing the details about the payment methods that need to be enabled under this merchant connector account
    #[schema(example = json!([
        {
            "payment_method": "wallet",
            "payment_method_types": [
                "upi_collect",
                "upi_intent"
            ],
            "payment_method_issuers": [
                "labore magna ipsum",
                "aute"
            ],
            "payment_schemes": [
                "Discover",
                "Discover"
            ],
            "accepted_currencies": {
                "type": "enable_only",
                "list": ["USD", "EUR"]
            },
            "accepted_countries": {
                "type": "disable_only",
                "list": ["FR", "DE","IN"]
            },
            "minimum_amount": 1,
            "maximum_amount": 68607706,
            "recurring_enabled": true,
            "installment_payment_enabled": true
        }
    ]))]
    pub payment_methods_enabled: Option<Vec<PaymentMethodsEnabled>>,

    /// Webhook details of this merchant connector
    #[schema(example = json!({
        "connector_webhook_details": {
            "merchant_secret": "1234567890987654321"
        }
    }))]
    pub connector_webhook_details: Option<MerchantConnectorWebhookDetails>,

    /// Metadata is useful for storing additional, unstructured information on an object.
    #[schema(value_type = Option<Object>,max_length = 255,example = json!({ "city": "NY", "unit": "245" }))]
    pub metadata: Option<pii::SecretSerdeValue>,

    /// A boolean value to indicate if the connector is in Test mode. By default, its value is false.
    #[schema(default = false, example = false)]
    pub test_mode: Option<bool>,

    /// A boolean value to indicate if the connector is disabled. By default, its value is false.
    #[schema(default = false, example = false)]
    pub disabled: Option<bool>,

    /// Contains the frm configs for the merchant connector
    #[schema(example = json!(consts::FRM_CONFIGS_EG))]
    pub frm_configs: Option<Vec<FrmConfigs>>,

    /// The business country to which the connector account is attached. To be deprecated soon. Use the 'profile_id' instead
    #[schema(value_type = Option<CountryAlpha2>, example = "US")]
    pub business_country: Option<api_enums::CountryAlpha2>,

    /// The business label to which the connector account is attached. To be deprecated soon. Use the 'profile_id' instead
    pub business_label: Option<String>,

    /// The business sublabel to which the connector account is attached. To be deprecated soon. Use the 'profile_id' instead
    #[schema(example = "chase")]
    pub business_sub_label: Option<String>,

    /// Unique ID of the connector
    #[schema(example = "mca_5apGeP94tMts6rg3U3kR", value_type = Option<String>)]
    pub merchant_connector_id: Option<id_type::MerchantConnectorAccountId>,

    #[schema(value_type = Option<Object>)]
    pub pm_auth_config: Option<pii::SecretSerdeValue>,

    #[schema(value_type = Option<ConnectorStatus>, example = "inactive")]
    pub status: Option<api_enums::ConnectorStatus>,

    /// In case the merchant needs to store any additional sensitive data
    #[schema(value_type = Option<AdditionalMerchantData>)]
    pub additional_merchant_data: Option<AdditionalMerchantData>,

    /// The connector_wallets_details is used to store wallet details such as certificates and wallet credentials
    #[schema(value_type = Option<ConnectorWalletDetails>)]
    pub connector_wallets_details: Option<ConnectorWalletDetails>,
}


impl api::MandateSetup for Digitalvirgo {}

impl api::MandateSetup for for Digitalvirgo


pub struct InespayPaymentWebhookData {
    pub single_payin_id: String,
    pub cod_status: InespayPSyncStatus,
    pub description: String,
    pub amount: MinorUnit,
    pub reference: String,
    pub creditor_account: Secret<String>,
    pub debtor_name: Secret<String>,
    pub debtor_account: Secret<String>,
    pub custom_data: Option<String>,
}


pub struct Card {
    number: cards::CardNumber,
    security_code: Secret<String>,
    expiry_date: ExpiryDate,
}


pub fn get_surcharge_keys() -> JsResult {
    let keys = <SurchargeDecisionConfigs as EuclidDirFilter>::ALLOWED;
    Ok(serde_wasm_bindgen::to_value(keys)?)
}