    HardDeclineIssueWithPaypalAccount,
    #[serde(rename = "613")]
    #[strum(serialize = "613")]
    HardDeclinePayPalAuthorizationIdMissing,
    #[serde(rename = "614")]
    #[strum(serialize = "614")]
    HardDeclineConfirmedEmailAddressIsNotAvailable,
    #[serde(rename = "615")]
    #[strum(serialize = "615")]
    HardDeclinePayPalBuyerAccountDenied,
    #[serde(rename = "616")]
    #[strum(serialize = "616")]
    HardDeclinePayPalBuyerAccountRestricted,
    #[serde(rename = "617")]
    #[strum(serialize = "617")]
    HardDeclinePayPalOrderHasBeenVoidedExpiredOrCompleted,
    #[serde(rename = "618")]
    #[strum(serialize = "618")]
    HardDeclineIssueWithPayPalRefund,
    #[serde(rename = "619")]
    #[strum(serialize = "619")]
    HardDeclinePayPalCredentialsIssue,
    #[serde(rename = "620")]
    #[strum(serialize = "620")]
    HardDeclinePayPalAuthorizationVoidedOrExpired,
    #[serde(rename = "621")]
    #[strum(serialize = "621")]
    HardDeclineRequiredPayPalParameterMissing,
    #[serde(rename = "622")]
    #[strum(serialize = "622")]
    HardDeclinePayPalTransactionIdOrAuthIdIsInvalid,
    #[serde(rename = "623")]
    #[strum(serialize = "623")]
    HardDeclineExceededMaximumNumberOfPayPalAuthorizationAttempts,
    #[serde(rename = "624")]
    #[strum(serialize = "624")]
    HardDeclineTransactionAmountExceedsMerchantsPayPalAccountLimit,
    #[serde(rename = "625")]
    #[strum(serialize = "625")]
    HardDeclinePayPalFundingSourcesUnavailable,
    #[serde(rename = "626")]
    #[strum(serialize = "626")]
    HardDeclineIssueWithPayPalPrimaryFundingSource,
    #[serde(rename = "627")]
    #[strum(serialize = "627")]
    HardDeclinePayPalProfileDoesNotAllowThisTransactionType,
    #[serde(rename = "628")]
    #[strum(serialize = "628")]
    InternalSystemErrorWithPayPalContactVantiv,
    #[serde(rename = "629")]
    #[strum(serialize = "629")]
    HardDeclineContactPayPalConsumerForAnotherPaymentMethod,
    #[serde(rename = "637")]
    #[strum(serialize = "637")]
    InvalidTerminalId,
    #[serde(rename = "640")]
    #[strum(serialize = "640")]
    PinlessDebitProcessingNotSupportedForNonRecurringTransactions,
    #[serde(rename = "641")]
    #[strum(serialize = "641")]
    PinlessDebitProcessingNotSupportedForPartialAuths,
    #[serde(rename = "642")]
    #[strum(serialize = "642")]
    MerchantNotConfiguredForPinlessDebitProcessing,
    #[serde(rename = "651")]
    #[strum(serialize = "651")]
    DeclineCustomerCancellation,
    #[serde(rename = "652")]
    #[strum(serialize = "652")]
    DeclineReTryTransaction,
    #[serde(rename = "653")]
    #[strum(serialize = "653")]
    DeclineUnableToLocateRecordOnFile,
    #[serde(rename = "654")]
    #[strum(serialize = "654")]
    DeclineFileUpdateFieldEditError,
    #[serde(rename = "655")]
    #[strum(serialize = "655")]
    RemoteFunctionUnknown,
    #[serde(rename = "656")]
    #[strum(serialize = "656")]
    DeclinedExceedsWithdrawalFrequencyLimit,
    #[serde(rename = "657")]
    #[strum(serialize = "657")]
    DeclineCardRecordNotAvailable,
    #[serde(rename = "658")]
    #[strum(serialize = "658")]
    InvalidAuthorizationCode,
    #[serde(rename = "659")]
    #[strum(serialize = "659")]
    ReconciliationError,
    #[serde(rename = "660")]
    #[strum(serialize = "660")]
    PreferredDebitRoutingDenialCreditTransactionCanBeDebit,
    #[serde(rename = "661")]
    #[strum(serialize = "661")]
    DeclinedCurrencyConversionCompleteNoAuthPerformed,
    #[serde(rename = "662")]
    #[strum(serialize = "662")]
    DeclinedMultiCurrencyDccFail,
    #[serde(rename = "663")]
    #[strum(serialize = "663")]
    DeclinedMultiCurrencyInvertFail,
    #[serde(rename = "664")]
    #[strum(serialize = "664")]
    Invalid3DSecurePassword,
    #[serde(rename = "665")]
    #[strum(serialize = "665")]
    InvalidSocialSecurityNumber,
    #[serde(rename = "666")]
    #[strum(serialize = "666")]
    InvalidMothersMaidenName,
    #[serde(rename = "667")]
    #[strum(serialize = "667")]
    EnrollmentInquiryDeclined,
    #[serde(rename = "668")]
    #[strum(serialize = "668")]
    SocialSecurityNumberNotAvailable,
    #[serde(rename = "669")]
    #[strum(serialize = "669")]
    MothersMaidenNameNotAvailable,
    #[serde(rename = "670")]
    #[strum(serialize = "670")]
    PinAlreadyExistsOnDatabase,
    #[serde(rename = "701")]
    #[strum(serialize = "701")]
    Under18YearsOld,
    #[serde(rename = "702")]
    #[strum(serialize = "702")]
    BillToOutsideUsa,
    #[serde(rename = "703")]
    #[strum(serialize = "703")]
    BillToAddressIsNotEqualToShipToAddress,
    #[serde(rename = "704")]
    #[strum(serialize = "704")]
    DeclinedForeignCurrencyMustBeUsd,
    #[serde(rename = "705")]
    #[strum(serialize = "705")]
    OnNegativeFile,
    #[serde(rename = "706")]
    #[strum(serialize = "706")]
    BlockedAgreement,
    #[serde(rename = "707")]
    #[strum(serialize = "707")]
    InsufficientBuyingPower,
    #[serde(rename = "708")]
    #[strum(serialize = "708")]
    InvalidData,
    #[serde(rename = "709")]
    #[strum(serialize = "709")]
    InvalidDataDataElementsMissing,
    #[serde(rename = "710")]
    #[strum(serialize = "710")]
    InvalidDataDataFormatError,
    #[serde(rename = "711")]
    #[strum(serialize = "711")]
    InvalidDataInvalidTCVersion,
    #[serde(rename = "712")]
    #[strum(serialize = "712")]
    DuplicateTransactionPaypalCredit,
    #[serde(rename = "713")]
    #[strum(serialize = "713")]
    VerifyBillingAddress,
    #[serde(rename = "714")]
    #[strum(serialize = "714")]
    InactiveAccount,
    #[serde(rename = "716")]
    #[strum(serialize = "716")]
    InvalidAuth,
    #[serde(rename = "717")]
    #[strum(serialize = "717")]
    AuthorizationAlreadyExistsForTheOrder,
    #[serde(rename = "730")]
    #[strum(serialize = "730")]
    LodgingTransactionsAreNotAllowedForThisMcc,
    #[serde(rename = "731")]
    #[strum(serialize = "731")]
    DurationCannotBeNegative,
    #[serde(rename = "732")]
    #[strum(serialize = "732")]
    HotelFolioNumberCannotBeBlank,
    #[serde(rename = "733")]
    #[strum(serialize = "733")]
    InvalidCheckInDate,
    #[serde(rename = "734")]
    #[strum(serialize = "734")]
    InvalidCheckOutDate,
    #[serde(rename = "735")]
    #[strum(serialize = "735")]
    InvalidCheckInOrCheckOutDate,
    #[serde(rename = "736")]
    #[strum(serialize = "736")]
    CheckOutDateCannotBeBeforeCheckInDate,
    #[serde(rename = "737")]
    #[strum(serialize = "737")]
    NumberOfAdultsCannotBeNegative,
    #[serde(rename = "738")]
    #[strum(serialize = "738")]
    RoomRateCannotBeNegative,
    #[serde(rename = "739")]
    #[strum(serialize = "739")]
    RoomTaxCannotBeNegative,
    #[serde(rename = "740")]
    #[strum(serialize = "740")]
    DurationCanOnlyBeFrom0To99ForVisa,
    #[serde(rename = "801")]
    #[strum(serialize = "801")]
    AccountNumberWasSuccessfullyRegistered,
    #[serde(rename = "802")]
    #[strum(serialize = "802")]
    AccountNumberWasPreviouslyRegistered,
    #[serde(rename = "803")]
    #[strum(serialize = "803")]
    ValidToken,
    #[serde(rename = "820")]
    #[strum(serialize = "820")]
    CreditCardNumberWasInvalid,
    #[serde(rename = "821")]
    #[strum(serialize = "821")]
    MerchantIsNotAuthorizedForTokens,
    #[serde(rename = "822")]
    #[strum(serialize = "822")]
    TokenWasNotFound,
    #[serde(rename = "823")]
    #[strum(serialize = "823")]
    TokenInvalid,
    #[serde(rename = "825")]
    #[strum(serialize = "825")]
    MerchantNotAuthorizedForECheckTokens,
    #[serde(rename = "826")]
    #[strum(serialize = "826")]
    CheckoutIdWasInvalid,
    #[serde(rename = "827")]
    #[strum(serialize = "827")]
    CheckoutIdWasNotFound,
    #[serde(rename = "828")]
    #[strum(serialize = "828")]
    GenericCheckoutIdError,
    #[serde(rename = "835")]
    #[strum(serialize = "835")]
    CaptureAmountCanNotBeMoreThanAuthorizedAmount,
    #[serde(rename = "850")]
    #[strum(serialize = "850")]
    TaxBillingOnlyAllowedForMcc9311,
    #[serde(rename = "851")]
    #[strum(serialize = "851")]
    Mcc9311RequiresTaxTypeElement,
    #[serde(rename = "852")]
    #[strum(serialize = "852")]
    DebtRepaymentOnlyAllowedForViTransactionsOnMccs6012And6051,
    #[serde(rename = "861")]
    #[strum(serialize = "861")]
    RoutingNumberDidNotMatchOneOnFileForToken,
    #[serde(rename = "877")]
    #[strum(serialize = "877")]
    InvalidPayPageRegistrationId,
    #[serde(rename = "878")]
    #[strum(serialize = "878")]
    ExpiredPayPageRegistrationId,
    #[serde(rename = "879")]
    #[strum(serialize = "879")]
    MerchantIsNotAuthorizedForPayPage,
    #[serde(rename = "890")]
    #[strum(serialize = "890")]
    MaximumNumberOfUpdatesForThisTokenExceeded,
    #[serde(rename = "891")]
    #[strum(serialize = "891")]
    TooManyTokensCreatedForExistingNamespace,
    #[serde(rename = "895")]
    #[strum(serialize = "895")]
    PinValidationNotPossible,
    #[serde(rename = "898")]
    #[strum(serialize = "898")]
    GenericTokenRegistrationError,
    #[serde(rename = "899")]
    #[strum(serialize = "899")]
    GenericTokenUseError,
    #[serde(rename = "900")]
    #[strum(serialize = "900")]
    InvalidBankRoutingNumber,
    #[serde(rename = "901")]
    #[strum(serialize = "901")]
    MissingName,
    #[serde(rename = "902")]
    #[strum(serialize = "902")]
    InvalidName,
    #[serde(rename = "903")]
    #[strum(serialize = "903")]
    MissingBillingCountryCode,
    #[serde(rename = "904")]
    #[strum(serialize = "904")]
    InvalidIban,
    #[serde(rename = "905")]
    #[strum(serialize = "905")]
    MissingEmailAddress,
    #[serde(rename = "906")]
    #[strum(serialize = "906")]
    MissingMandateReference,
    #[serde(rename = "907")]
    #[strum(serialize = "907")]
    InvalidMandateReference,
    #[serde(rename = "908")]
    #[strum(serialize = "908")]
    MissingMandateUrl,
    #[serde(rename = "909")]
    #[strum(serialize = "909")]
    InvalidMandateUrl,
    #[serde(rename = "911")]
    #[strum(serialize = "911")]
    MissingMandateSignatureDate,
    #[serde(rename = "912")]
    #[strum(serialize = "912")]
    InvalidMandateSignatureDate,
    #[serde(rename = "913")]
    #[strum(serialize = "913")]
    RecurringMandateAlreadyExists,
    #[serde(rename = "914")]
    #[strum(serialize = "914")]
    RecurringMandateWasNotFound,
    #[serde(rename = "915")]
    #[strum(serialize = "915")]
    FinalRecurringWasAlreadyReceivedUsingThisMandate,
    #[serde(rename = "916")]
    #[strum(serialize = "916")]
    IbanDidNotMatchOneOnFileForMandate,
    #[serde(rename = "917")]
    #[strum(serialize = "917")]
    InvalidBillingCountry,
    #[serde(rename = "922")]
    #[strum(serialize = "922")]
    ExpirationDateRequiredForInteracTransaction,
    #[serde(rename = "923")]
    #[strum(serialize = "923")]
    TransactionTypeIsNotSupportedWithThisMethodOfPayment,
    #[serde(rename = "924")]
    #[strum(serialize = "924")]
    UnreferencedOrphanRefundsAreNotAllowed,
    #[serde(rename = "939")]
    #[strum(serialize = "939")]
    UnableToVoidATransactionWithAHeldState,
    #[serde(rename = "940")]
    #[strum(serialize = "940")]
    ThisFundingInstructionResultsInANegativeAccountBalance,
    #[serde(rename = "941")]
    #[strum(serialize = "941")]
    AccountBalanceInformationUnavailableAtThisTime,
    #[serde(rename = "942")]
    #[strum(serialize = "942")]
    TheSubmittedCardIsNotEligibleForFastAccessFunding,
    #[serde(rename = "943")]
    #[strum(serialize = "943")]
    TransactionCannotUseBothCcdPaymentInformationAndCtxPaymentInformation,
    #[serde(rename = "944")]
    #[strum(serialize = "944")]
    ProcessingError,
    #[serde(rename = "945")]
    #[strum(serialize = "945")]
    ThisFundingInstructionTypeIsInvalidForCanadianMerchants,
    #[serde(rename = "946")]
    #[strum(serialize = "946")]
    CtxAndCcdRecordsAreNotAllowedForCanadianMerchants,
    #[serde(rename = "947")]
    #[strum(serialize = "947")]
    CanadianAccountNumberCannotExceed12Digits,
    #[serde(rename = "948")]
    #[strum(serialize = "948")]
    ThisFundingInstructionTypeIsInvalid,
    #[serde(rename = "950")]
    #[strum(serialize = "950")]
    DeclineNegativeInformationOnFile,
    #[serde(rename = "951")]
    #[strum(serialize = "951")]
    AbsoluteDecline,
    #[serde(rename = "952")]
    #[strum(serialize = "952")]
    TheMerchantProfileDoesNotAllowTheRequestedOperation,
    #[serde(rename = "953")]
    #[strum(serialize = "953")]
    TheAccountCannotAcceptAchTransactions,
    #[serde(rename = "954")]
    #[strum(serialize = "954")]
    TheAccountCannotAcceptAchTransactionsOrSiteDrafts,
    #[serde(rename = "955")]
    #[strum(serialize = "955")]
    AmountGreaterThanLimitSpecifiedInTheMerchantProfile,
    #[serde(rename = "956")]
    #[strum(serialize = "956")]
    MerchantIsNotAuthorizedToPerformECheckVerificationTransactions,
    #[serde(rename = "957")]
    #[strum(serialize = "957")]
    FirstNameAndLastNameRequiredForECheckVerifications,
    #[serde(rename = "958")]
    #[strum(serialize = "958")]
    CompanyNameRequiredForCorporateAccountForECheckVerifications,
    #[serde(rename = "959")]
    #[strum(serialize = "959")]
    PhoneNumberRequiredForECheckVerifications,
    #[serde(rename = "961")]
    #[strum(serialize = "961")]
    CardBrandTokenNotSupported,
    #[serde(rename = "962")]
    #[strum(serialize = "962")]
    PrivateLabelCardNotSupported,
    #[serde(rename = "965")]
    #[strum(serialize = "965")]
    AllowedDailyDirectDebitCaptureECheckSaleLimitExceeded,
    #[serde(rename = "966")]
    #[strum(serialize = "966")]
    AllowedDailyDirectDebitCreditECheckCreditLimitExceeded,
    #[serde(rename = "973")]
    #[strum(serialize = "973")]
    AccountNotEligibleForRtp,
    #[serde(rename = "980")]
    #[strum(serialize = "980")]
    SoftDeclineCustomerAuthenticationRequired,
    #[serde(rename = "981")]
    #[strum(serialize = "981")]
    TransactionNotReversedVoidWorkflowNeedToBeInvoked,
    #[serde(rename = "982")]
    #[strum(serialize = "982")]
    TransactionReversalNotSupportedForTheCoreMerchants,
    #[serde(rename = "983")]
    #[strum(serialize = "983")]
    NoValidParentDepositOrParentRefundFound,
    #[serde(rename = "984")]
    #[strum(serialize = "984")]
    TransactionReversalNotEnabledForVisa,
    #[serde(rename = "985")]
    #[strum(serialize = "985")]
    TransactionReversalNotEnabledForMastercard,
    #[serde(rename = "986")]
    #[strum(serialize = "986")]
    TransactionReversalNotEnabledForAmEx,
    #[serde(rename = "987")]
    #[strum(serialize = "987")]
    TransactionReversalNotEnabledForDiscover,
    #[serde(rename = "988")]
    #[strum(serialize = "988")]
    TransactionReversalNotSupported,
    #[serde(rename = "990")]
    #[strum(serialize = "990")]
    FundingInstructionHeldPleaseContactYourRelationshipManager,
    #[serde(rename = "991")]
    #[strum(serialize = "991")]
    MissingAddressInformation,
    #[serde(rename = "992")]
    #[strum(serialize = "992")]
    CryptographicFailure,
    #[serde(rename = "993")]
    #[strum(serialize = "993")]
    InvalidRegionCode,
    #[serde(rename = "994")]
    #[strum(serialize = "994")]
    InvalidCountryCode,
    #[serde(rename = "995")]
    #[strum(serialize = "995")]
    InvalidCreditAccount,
    #[serde(rename = "996")]
    #[strum(serialize = "996")]
    InvalidCheckingAccount,
    #[serde(rename = "997")]
    #[strum(serialize = "997")]
    InvalidSavingsAccount,
    #[serde(rename = "998")]
    #[strum(serialize = "998")]
    InvalidUseOfMccCorrectAndReattempt,
    #[serde(rename = "999")]
    #[strum(serialize = "999")]
    ExceedsRtpTransactionLimit,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
#[serde(rename_all = "lowercase")]
pub enum WorldpayvantivPaymentFlow {
    Sale,
    Auth,
    Capture,
    Void,
    //VoidPostCapture
    VoidPC,
}

fn get_payment_flow_type(input: &str) -> Result<WorldpayvantivPaymentFlow, errors::ConnectorError> {
    if input.contains("auth") {
        Ok(WorldpayvantivPaymentFlow::Auth)
    } else if input.contains("sale") {
        Ok(WorldpayvantivPaymentFlow::Sale)
    } else if input.contains("voidpc") {
        Ok(WorldpayvantivPaymentFlow::VoidPC)
    } else if input.contains("void") {
        Ok(WorldpayvantivPaymentFlow::Void)
    } else if input.contains("capture") {
        Ok(WorldpayvantivPaymentFlow::Capture)
    } else {
        Err(errors::ConnectorError::UnexpectedResponseError(
            bytes::Bytes::from("Invalid merchantTxnId".to_string()),
        ))
    }
}

fn get_attempt_status(
    flow: WorldpayvantivPaymentFlow,
    response: WorldpayvantivResponseCode,
) -> Result<common_enums::AttemptStatus, errors::ConnectorError> {
    match response {
        WorldpayvantivResponseCode::Approved
            | WorldpayvantivResponseCode::PartiallyApproved
            | WorldpayvantivResponseCode::OfflineApproval
            | WorldpayvantivResponseCode::OfflineApprovalUnableToGoOnline
            | WorldpayvantivResponseCode::ConsumerNonReloadablePrepaidCardApproved
            | WorldpayvantivResponseCode::ConsumerSingleUseVirtualCardNumberApproved
            | WorldpayvantivResponseCode::ScheduledRecurringPaymentProcessed
            | WorldpayvantivResponseCode::ApprovedRecurringSubscriptionCreated
            | WorldpayvantivResponseCode::PendingShopperCheckoutCompletion
            | WorldpayvantivResponseCode::TransactionReceived
            | WorldpayvantivResponseCode::AccountNumberWasSuccessfullyRegistered
            | WorldpayvantivResponseCode::AccountNumberWasPreviouslyRegistered
            | WorldpayvantivResponseCode::ValidToken
             => match flow {
                WorldpayvantivPaymentFlow::Sale => Ok(common_enums::AttemptStatus::Pending),
                WorldpayvantivPaymentFlow::Auth => Ok(common_enums::AttemptStatus::Authorizing),
                WorldpayvantivPaymentFlow::Capture => Ok(common_enums::AttemptStatus::CaptureInitiated),
                WorldpayvantivPaymentFlow::Void => Ok(common_enums::AttemptStatus::VoidInitiated),
                WorldpayvantivPaymentFlow::VoidPC => {
                    Ok(common_enums::AttemptStatus::VoidInitiated)
                }
            },
        WorldpayvantivResponseCode::ShopperCheckoutExpired
            | WorldpayvantivResponseCode::ProcessingNetworkUnavailable
            | WorldpayvantivResponseCode::IssuerUnavailable
            | WorldpayvantivResponseCode::ReSubmitTransaction
            | WorldpayvantivResponseCode::TryAgainLater
            | WorldpayvantivResponseCode::InsufficientFunds
            | WorldpayvantivResponseCode::AuthorizationAmountHasAlreadyBeenDepleted
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter1Hour
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter24Hour
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter2Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter4Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter6Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter8Days
            | WorldpayvantivResponseCode::InsufficientFundsRetryAfter10Days
            | WorldpayvantivResponseCode::CallIssuer
            | WorldpayvantivResponseCode::CallAmex
            | WorldpayvantivResponseCode::CallDinersClub
            | WorldpayvantivResponseCode::CallDiscover
            | WorldpayvantivResponseCode::CallJbs
            | WorldpayvantivResponseCode::CallVisaMastercard
            | WorldpayvantivResponseCode::CallIssuerUpdateCardholderData
            | WorldpayvantivResponseCode::ExceedsApprovalAmountLimit
            | WorldpayvantivResponseCode::CallIndicatedNumber
            | WorldpayvantivResponseCode::UnacceptablePinTransactionDeclinedRetry
            | WorldpayvantivResponseCode::PinNotChanged
            | WorldpayvantivResponseCode::ConsumerMultiUseVirtualCardNumberSoftDecline
            | WorldpayvantivResponseCode::ConsumerNonReloadablePrepaidCardSoftDecline
            | WorldpayvantivResponseCode::ConsumerSingleUseVirtualCardNumberSoftDecline
            | WorldpayvantivResponseCode::UpdateCardholderData
            | WorldpayvantivResponseCode::MerchantDoesntQualifyForProductCode
            | WorldpayvantivResponseCode::Lifecycle
            | WorldpayvantivResponseCode::Policy
            | WorldpayvantivResponseCode::FraudSecurity
            | WorldpayvantivResponseCode::InvalidOrExpiredCardContactCardholderToUpdate
            | WorldpayvantivResponseCode::InvalidTransactionOrCardRestrictionVerifyInformationAndResubmit
            | WorldpayvantivResponseCode::AtLeastOneOfOrigIdOrOrigCnpTxnIdIsRequired
            | WorldpayvantivResponseCode::OrigCnpTxnIdIsRequiredWhenShowStatusOnlyIsUsed
            | WorldpayvantivResponseCode::IncrementalAuthNotSupported
            | WorldpayvantivResponseCode::SetAuthIndicatorToIncremental
            | WorldpayvantivResponseCode::IncrementalValueForAuthIndicatorNotAllowedInThisAuthStructure
            | WorldpayvantivResponseCode::CannotRequestAnIncrementalAuthIfOriginalAuthNotSetToEstimated
            | WorldpayvantivResponseCode::TransactionMustReferenceTheEstimatedAuth
            | WorldpayvantivResponseCode::IncrementedAuthExceedsMaxTransactionAmount
            | WorldpayvantivResponseCode::SubmittedMccNotAllowed
            | WorldpayvantivResponseCode::MerchantNotCertifiedEnabledForIias
            | WorldpayvantivResponseCode::IssuerGeneratedError
            | WorldpayvantivResponseCode::PickupCardOtherThanLostStolen
            | WorldpayvantivResponseCode::InvalidAmountHardDecline
            | WorldpayvantivResponseCode::ReversalUnsuccessful
            | WorldpayvantivResponseCode::MissingData
            | WorldpayvantivResponseCode::PickupCardLostCard
            | WorldpayvantivResponseCode::PickupCardStolenCard
            | WorldpayvantivResponseCode::RestrictedCard
            | WorldpayvantivResponseCode::InvalidDeactivate
            | WorldpayvantivResponseCode::CardAlreadyActive
            | WorldpayvantivResponseCode::CardNotActive
            | WorldpayvantivResponseCode::CardAlreadyDeactivate
            | WorldpayvantivResponseCode::OverMaxBalance
            | WorldpayvantivResponseCode::InvalidActivate
            | WorldpayvantivResponseCode::NoTransactionFoundForReversal
            | WorldpayvantivResponseCode::IncorrectCvv
            | WorldpayvantivResponseCode::IllegalTransaction
            | WorldpayvantivResponseCode::DuplicateTransaction
            | WorldpayvantivResponseCode::SystemError
            | WorldpayvantivResponseCode::DeconvertedBin
            | WorldpayvantivResponseCode::MerchantDepleted
            | WorldpayvantivResponseCode::GiftCardEscheated
            | WorldpayvantivResponseCode::InvalidReversalTypeForCreditCardTransaction
            | WorldpayvantivResponseCode::SystemErrorMessageFormatError
            | WorldpayvantivResponseCode::SystemErrorCannotProcess
            | WorldpayvantivResponseCode::RefundRejectedDueToPendingDepositStatus
            | WorldpayvantivResponseCode::RefundRejectedDueToDeclinedDepositStatus
            | WorldpayvantivResponseCode::RefundRejectedByTheProcessingNetwork
            | WorldpayvantivResponseCode::CaptureCreditAndAuthReversalTagsCannotBeUsedForGiftCardTransactions
            | WorldpayvantivResponseCode::InvalidAccountNumber
            | WorldpayvantivResponseCode::AccountNumberDoesNotMatchPaymentType
            | WorldpayvantivResponseCode::PickUpCard
            | WorldpayvantivResponseCode::LostStolenCard
            | WorldpayvantivResponseCode::ExpiredCard
            | WorldpayvantivResponseCode::AuthorizationHasExpiredNoNeedToReverse
            | WorldpayvantivResponseCode::RestrictedCardSoftDecline
            | WorldpayvantivResponseCode::RestrictedCardChargeback
            | WorldpayvantivResponseCode::RestrictedCardPrepaidCardFilteringService
            | WorldpayvantivResponseCode::InvalidTrackData
            | WorldpayvantivResponseCode::DepositIsAlreadyReferencedByAChargeback
            | WorldpayvantivResponseCode::RestrictedCardInternationalCardFilteringService
            | WorldpayvantivResponseCode::InternationalFilteringForIssuingCardCountry
            | WorldpayvantivResponseCode::RestrictedCardAuthFraudVelocityFilteringService
            | WorldpayvantivResponseCode::AutomaticRefundAlreadyIssued
            | WorldpayvantivResponseCode::RestrictedCardAuthFraudAdviceFilteringService
            | WorldpayvantivResponseCode::RestrictedCardFraudAvsFilteringService
            |  WorldpayvantivResponseCode::InvalidExpirationDate
            | WorldpayvantivResponseCode::InvalidMerchant
            | WorldpayvantivResponseCode::InvalidTransaction
            | WorldpayvantivResponseCode::NoSuchIssuer
            | WorldpayvantivResponseCode::InvalidPin
            | WorldpayvantivResponseCode::TransactionNotAllowedAtTerminal
            | WorldpayvantivResponseCode::ExceedsNumberOfPinEntries
            | WorldpayvantivResponseCode::CardholderTransactionNotPermitted
            | WorldpayvantivResponseCode::CardholderRequestedThatRecurringOrInstallmentPaymentBeStopped
            | WorldpayvantivResponseCode::InvalidPaymentType
            | WorldpayvantivResponseCode::InvalidPosCapabilityForCardholderAuthorizedTerminalTransaction
            | WorldpayvantivResponseCode::InvalidPosCardholderIdForCardholderAuthorizedTerminalTransaction
            | WorldpayvantivResponseCode::ThisMethodOfPaymentDoesNotSupportAuthorizationReversals
            | WorldpayvantivResponseCode::ReversalAmountDoesNotMatchAuthorizationAmount
            | WorldpayvantivResponseCode::TransactionDidNotConvertToPinless
            | WorldpayvantivResponseCode::InvalidAmountSoftDecline
            | WorldpayvantivResponseCode::InvalidHealthcareAmounts
            | WorldpayvantivResponseCode::InvalidBillingDescriptorPrefix
            | WorldpayvantivResponseCode::InvalidBillingDescriptor
            | WorldpayvantivResponseCode::InvalidReportGroup
            | WorldpayvantivResponseCode::DoNotHonor
            | WorldpayvantivResponseCode::GenericDecline
            | WorldpayvantivResponseCode::DeclineRequestPositiveId
            | WorldpayvantivResponseCode::DeclineCvv2CidFail
            | WorldpayvantivResponseCode::ThreeDSecureTransactionNotSupportedByMerchant
            | WorldpayvantivResponseCode::InvalidPurchaseLevelIiiTheTransactionContainedBadOrMissingData
            | WorldpayvantivResponseCode::MissingHealthcareIiasTagForAnFsaTransaction
            | WorldpayvantivResponseCode::RestrictedByVantivDueToSecurityCodeMismatch
            | WorldpayvantivResponseCode::NoTransactionFoundWithSpecifiedTransactionId
            | WorldpayvantivResponseCode::AuthorizationNoLongerAvailable
            | WorldpayvantivResponseCode::TransactionNotVoidedAlreadySettled
            | WorldpayvantivResponseCode::AutoVoidOnRefund
            | WorldpayvantivResponseCode::InvalidAccountNumberOriginalOrNocUpdatedECheckAccountRequired
            | WorldpayvantivResponseCode::TotalCreditAmountExceedsCaptureAmount
            | WorldpayvantivResponseCode::ExceedTheThresholdForSendingRedeposits
            | WorldpayvantivResponseCode::DepositHasNotBeenReturnedForInsufficientNonSufficientFunds
            | WorldpayvantivResponseCode::InvalidCheckNumber
            | WorldpayvantivResponseCode::RedepositAgainstInvalidTransactionType
            | WorldpayvantivResponseCode::InternalSystemErrorCallVantiv
            | WorldpayvantivResponseCode::OriginalTransactionHasBeenProcessedFutureRedepositsCanceled
            | WorldpayvantivResponseCode::SoftDeclineAutoRecyclingInProgress
            | WorldpayvantivResponseCode::HardDeclineAutoRecyclingComplete
            | WorldpayvantivResponseCode::RestrictedCardCardUnderSanction
            | WorldpayvantivResponseCode::MerchantIsNotEnabledForSurcharging
            | WorldpayvantivResponseCode::ThisMethodOfPaymentDoesNotSupportSurcharging
            | WorldpayvantivResponseCode::SurchargeIsNotValidForDebitOrPrepaidCards
            | WorldpayvantivResponseCode::SurchargeCannotExceedsTheMaximumAllowedLimit
            | WorldpayvantivResponseCode::TransactionDeclinedByTheProcessingNetwork
            | WorldpayvantivResponseCode::SecondaryAmountCannotExceedTheSaleAmount
            | WorldpayvantivResponseCode::ThisMethodOfPaymentDoesNotSupportSecondaryAmount
            | WorldpayvantivResponseCode::SecondaryAmountCannotBeLessThanZero
            | WorldpayvantivResponseCode::PartialTransactionIsNotSupportedWhenIncludingASecondaryAmount
            | WorldpayvantivResponseCode::SecondaryAmountRequiredOnPartialRefundWhenUsedOnDeposit
            | WorldpayvantivResponseCode::SecondaryAmountNotAllowedOnRefundIfNotIncludedOnDeposit
            | WorldpayvantivResponseCode::ProcessingNetworkError
            | WorldpayvantivResponseCode::InvalidEMail
            | WorldpayvantivResponseCode::InvalidCombinationOfAccountFundingTransactionTypeAndMcc
            | WorldpayvantivResponseCode::InvalidAccountFundingTransactionTypeForThisMethodOfPayment
            | WorldpayvantivResponseCode::MissingOneOrMoreReceiverFieldsForAccountFundingTransaction
            | WorldpayvantivResponseCode::InvalidRecurringRequestSeeRecurringResponseForDetails
            | WorldpayvantivResponseCode::ParentTransactionDeclinedRecurringSubscriptionNotCreated
            | WorldpayvantivResponseCode::InvalidPlanCode
            | WorldpayvantivResponseCode::InvalidSubscriptionId
            | WorldpayvantivResponseCode::AddOnCodeAlreadyExists
            | WorldpayvantivResponseCode::DuplicateAddOnCodesInRequests
            | WorldpayvantivResponseCode::NoMatchingAddOnCodeForTheSubscription
            | WorldpayvantivResponseCode::NoMatchingDiscountCodeForTheSubscription
            | WorldpayvantivResponseCode::DuplicateDiscountCodesInRequest
            | WorldpayvantivResponseCode::InvalidStartDate
            | WorldpayvantivResponseCode::MerchantNotRegisteredForRecurringEngine
            | WorldpayvantivResponseCode::InsufficientDataToUpdateSubscription
            | WorldpayvantivResponseCode::InvalidBillingDate
            | WorldpayvantivResponseCode::DiscountCodeAlreadyExists
            | WorldpayvantivResponseCode::PlanCodeAlreadyExists
            | WorldpayvantivResponseCode::TheAccountNumberWasChanged
            | WorldpayvantivResponseCode::TheAccountWasClosed
            | WorldpayvantivResponseCode::TheExpirationDateWasChanged
            | WorldpayvantivResponseCode::TheIssuingBankDoesNotParticipateInTheUpdateProgram
            | WorldpayvantivResponseCode::ContactTheCardholderForUpdatedInformation


// File: crates/router_derive/src/macros/operation.rs
// Module: router_derive
// Public functions: 1
// Public structs: 1

use std::str::FromStr;

use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use strum::IntoEnumIterator;
use syn::{self, parse::Parse, DeriveInput};

use crate::macros::helpers;

#[derive(Debug, Clone, Copy, strum::EnumString, strum::EnumIter, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum Derives {
    Sync,
    Cancel,
    Reject,
    Capture,
    ApproveData,
    Authorize,
    AuthorizeData,
    SyncData,
    CancelData,
    CancelPostCapture,
    CancelPostCaptureData,
    CaptureData,
    CompleteAuthorizeData,
    RejectData,
    SetupMandateData,
    Start,
    Verify,
    Session,
    SessionData,
    IncrementalAuthorization,
    IncrementalAuthorizationData,
    SdkSessionUpdate,
    SdkSessionUpdateData,
    PostSessionTokens,
    PostSessionTokensData,
    UpdateMetadata,
    UpdateMetadataData,
}

impl Derives {
    fn to_operation(
        self,
        fns: impl Iterator<Item = TokenStream> + Clone,
        struct_name: &syn::Ident,
    ) -> TokenStream {
        let req_type = Conversion::get_req_type(self);
        quote! {
            #[automatically_derived]
            impl<F:Send+Clone+Sync> Operation<F,#req_type> for #struct_name {
                type Data = PaymentData<F>;
                #(#fns)*
            }
        }
    }

    fn to_ref_operation(
        self,
        ref_fns: impl Iterator<Item = TokenStream> + Clone,
        struct_name: &syn::Ident,
    ) -> TokenStream {
        let req_type = Conversion::get_req_type(self);
        quote! {
            #[automatically_derived]
            impl<F:Send+Clone+Sync> Operation<F,#req_type> for &#struct_name {
                type Data = PaymentData<F>;
                #(#ref_fns)*
            }
        }
    }
}

#[derive(Debug, Clone, strum::EnumString, strum::EnumIter, strum::Display)]
#[strum(serialize_all = "snake_case")]
pub enum Conversion {
    ValidateRequest,
    GetTracker,
    Domain,
    UpdateTracker,
    PostUpdateTracker,
    All,
    Invalid(String),
}

impl Conversion {
    fn get_req_type(ident: Derives) -> syn::Ident {
        match ident {
            Derives::Authorize => syn::Ident::new("PaymentsRequest", Span::call_site()),
            Derives::AuthorizeData => syn::Ident::new("PaymentsAuthorizeData", Span::call_site()),
            Derives::Sync => syn::Ident::new("PaymentsRetrieveRequest", Span::call_site()),
            Derives::SyncData => syn::Ident::new("PaymentsSyncData", Span::call_site()),
            Derives::Cancel => syn::Ident::new("PaymentsCancelRequest", Span::call_site()),
            Derives::CancelData => syn::Ident::new("PaymentsCancelData", Span::call_site()),
            Derives::ApproveData => syn::Ident::new("PaymentsApproveData", Span::call_site()),
            Derives::Reject => syn::Ident::new("PaymentsRejectRequest", Span::call_site()),
            Derives::RejectData => syn::Ident::new("PaymentsRejectData", Span::call_site()),
            Derives::Capture => syn::Ident::new("PaymentsCaptureRequest", Span::call_site()),
            Derives::CaptureData => syn::Ident::new("PaymentsCaptureData", Span::call_site()),
            Derives::CompleteAuthorizeData => {
                syn::Ident::new("CompleteAuthorizeData", Span::call_site())
            }
            Derives::Start => syn::Ident::new("PaymentsStartRequest", Span::call_site()),
            Derives::Verify => syn::Ident::new("VerifyRequest", Span::call_site()),
            Derives::SetupMandateData => {
                syn::Ident::new("SetupMandateRequestData", Span::call_site())
            }
            Derives::Session => syn::Ident::new("PaymentsSessionRequest", Span::call_site()),
            Derives::SessionData => syn::Ident::new("PaymentsSessionData", Span::call_site()),
            Derives::IncrementalAuthorization => {
                syn::Ident::new("PaymentsIncrementalAuthorizationRequest", Span::call_site())
            }
            Derives::IncrementalAuthorizationData => {
                syn::Ident::new("PaymentsIncrementalAuthorizationData", Span::call_site())
            }
            Derives::SdkSessionUpdate => {
                syn::Ident::new("PaymentsDynamicTaxCalculationRequest", Span::call_site())
            }
            Derives::SdkSessionUpdateData => {
                syn::Ident::new("SdkPaymentsSessionUpdateData", Span::call_site())
            }
            Derives::PostSessionTokens => {
                syn::Ident::new("PaymentsPostSessionTokensRequest", Span::call_site())
            }
            Derives::PostSessionTokensData => {
                syn::Ident::new("PaymentsPostSessionTokensData", Span::call_site())
            }
            Derives::UpdateMetadata => {
                syn::Ident::new("PaymentsUpdateMetadataRequest", Span::call_site())
            }
            Derives::UpdateMetadataData => {
                syn::Ident::new("PaymentsUpdateMetadataData", Span::call_site())
            }
            Derives::CancelPostCapture => {
                syn::Ident::new("PaymentsCancelPostCaptureRequest", Span::call_site())
            }
            Derives::CancelPostCaptureData => {
                syn::Ident::new("PaymentsCancelPostCaptureData", Span::call_site())
            }
        }
    }

    fn to_function(&self, ident: Derives) -> TokenStream {
        let req_type = Self::get_req_type(ident);
        match self {
            Self::ValidateRequest => quote! {
                fn to_validate_request(&self) -> RouterResult<&(dyn ValidateRequest<F,#req_type, Self::Data> + Send + Sync)> {
                    Ok(self)
                }
            },
            Self::GetTracker => quote! {
                fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<F, Self::Data, #req_type> + Send + Sync)> {
                    Ok(self)
                }
            },
            Self::Domain => quote! {
                fn to_domain(&self) -> RouterResult<&dyn Domain<F,#req_type, Self::Data>> {
                    Ok(self)
                }
            },
            Self::UpdateTracker => quote! {
                fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<F, Self::Data, #req_type> + Send + Sync)> {
                    Ok(self)
                }
            },
            Self::PostUpdateTracker => quote! {
                fn to_post_update_tracker(&self) -> RouterResult<&(dyn PostUpdateTracker<F, Self::Data, #req_type> + Send + Sync)> {
                    Ok(self)
                }
            },
            Self::Invalid(s) => {
                helpers::syn_error(Span::call_site(), &format!("Invalid identifier {s}"))
                    .to_compile_error()
            }
            Self::All => {
                let validate_request = Self::ValidateRequest.to_function(ident);
                let get_tracker = Self::GetTracker.to_function(ident);
                let domain = Self::Domain.to_function(ident);
                let update_tracker = Self::UpdateTracker.to_function(ident);

                quote! {
                    #validate_request
                    #get_tracker
                    #domain
                    #update_tracker
                }
            }
        }
    }

    fn to_ref_function(&self, ident: Derives) -> TokenStream {
        let req_type = Self::get_req_type(ident);
        match self {
            Self::ValidateRequest => quote! {
                fn to_validate_request(&self) -> RouterResult<&(dyn ValidateRequest<F, #req_type, Self::Data> + Send + Sync)> {
                    Ok(*self)
                }
            },
            Self::GetTracker => quote! {
                fn to_get_tracker(&self) -> RouterResult<&(dyn GetTracker<F, Self::Data,#req_type> + Send + Sync)> {
                    Ok(*self)
                }
            },
            Self::Domain => quote! {
                fn to_domain(&self) -> RouterResult<&(dyn Domain<F,#req_type, Self::Data>)> {
                    Ok(*self)
                }
            },
            Self::UpdateTracker => quote! {
                fn to_update_tracker(&self) -> RouterResult<&(dyn UpdateTracker<F, Self::Data,#req_type> + Send + Sync)> {
                    Ok(*self)
                }
            },
            Self::PostUpdateTracker => quote! {
                fn to_post_update_tracker(&self) -> RouterResult<&(dyn PostUpdateTracker<F, Self::Data, #req_type> + Send + Sync)> {
                    Ok(*self)
                }
            },
            Self::Invalid(s) => {
                helpers::syn_error(Span::call_site(), &format!("Invalid identifier {s}"))
                    .to_compile_error()
            }
            Self::All => {
                let validate_request = Self::ValidateRequest.to_ref_function(ident);
                let get_tracker = Self::GetTracker.to_ref_function(ident);
                let domain = Self::Domain.to_ref_function(ident);
                let update_tracker = Self::UpdateTracker.to_ref_function(ident);

                quote! {
                    #validate_request
                    #get_tracker
                    #domain
                    #update_tracker
                }
            }
        }
    }
}

mod operations_keyword {
    use syn::custom_keyword;

    custom_keyword!(operations);
    custom_keyword!(flow);
}

#[derive(Debug)]
pub enum OperationsEnumMeta {
    Operations {
        keyword: operations_keyword::operations,
        value: Vec<Conversion>,
    },
    Flow {
        keyword: operations_keyword::flow,
        value: Vec<Derives>,
    },
}

#[derive(Clone)]
pub struct OperationProperties {
    operations: Vec<Conversion>,
    flows: Vec<Derives>,
}

fn get_operation_properties(
    operation_enums: Vec<OperationsEnumMeta>,
) -> syn::Result<OperationProperties> {
    let mut operations = vec![];
    let mut flows = vec![];

    for operation in operation_enums {
        match operation {
            OperationsEnumMeta::Operations { value, .. } => {
                operations = value;
            }
            OperationsEnumMeta::Flow { value, .. } => {
                flows = value;
            }
        }
    }

    if operations.is_empty() {
        Err(syn::Error::new(
            Span::call_site(),
            "atleast one operation must be specitied",
        ))?;
    }

    if flows.is_empty() {
        Err(syn::Error::new(
            Span::call_site(),
            "atleast one flow must be specitied",
        ))?;
    }

    Ok(OperationProperties { operations, flows })
}

impl Parse for Derives {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let text = input.parse::<syn::LitStr>()?;
        let value = text.value();

        value.as_str().parse().map_err(|_| {
            syn::Error::new_spanned(
                &text,
                format!(
                    "Unexpected value for flow: `{value}`. Possible values are `{}`",
                    helpers::get_possible_values_for_enum::<Self>()
                ),
            )
        })
    }
}

impl Parse for Conversion {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let text = input.parse::<syn::LitStr>()?;
        let value = text.value();

        value.as_str().parse().map_err(|_| {
            syn::Error::new_spanned(
                &text,
                format!(
                    "Unexpected value for operation: `{value}`. Possible values are `{}`",
                    helpers::get_possible_values_for_enum::<Self>()
                ),
            )
        })
    }
}

fn parse_list_string<T>(list_string: String, keyword: &str) -> syn::Result<Vec<T>>
where
    T: FromStr + IntoEnumIterator + ToString,
{
    list_string
        .split(',')
        .map(str::trim)
        .map(T::from_str)
        .map(|result| {
            result.map_err(|_| {
                syn::Error::new(
                    Span::call_site(),
                    format!(
                        "Unexpected {keyword}, possible values are {}",
                        helpers::get_possible_values_for_enum::<T>()
                    ),
                )
            })
        })
        .collect()
}

fn get_conversions(input: syn::parse::ParseStream<'_>) -> syn::Result<Vec<Conversion>> {
    let lit_str_list = input.parse::<syn::LitStr>()?;
    parse_list_string(lit_str_list.value(), "operation")
}

fn get_derives(input: syn::parse::ParseStream<'_>) -> syn::Result<Vec<Derives>> {
    let lit_str_list = input.parse::<syn::LitStr>()?;
    parse_list_string(lit_str_list.value(), "flow")
}

impl Parse for OperationsEnumMeta {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(operations_keyword::operations) {
            let keyword = input.parse()?;
            input.parse::<syn::Token![=]>()?;
            let value = get_conversions(input)?;
            Ok(Self::Operations { keyword, value })
        } else if lookahead.peek(operations_keyword::flow) {
            let keyword = input.parse()?;
            input.parse::<syn::Token![=]>()?;
            let value = get_derives(input)?;
            Ok(Self::Flow { keyword, value })
        } else {
            Err(lookahead.error())
        }
    }
}

trait OperationsDeriveInputExt {
    /// Get all the error metadata associated with an enum.
    fn get_metadata(&self) -> syn::Result<Vec<OperationsEnumMeta>>;
}

impl OperationsDeriveInputExt for DeriveInput {
    fn get_metadata(&self) -> syn::Result<Vec<OperationsEnumMeta>> {
        helpers::get_metadata_inner("operation", &self.attrs)
    }
}

impl ToTokens for OperationsEnumMeta {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        match self {
            Self::Operations { keyword, .. } => keyword.to_tokens(tokens),
            Self::Flow { keyword, .. } => keyword.to_tokens(tokens),
        }
    }
}

pub fn operation_derive_inner(input: DeriveInput) -> syn::Result<proc_macro::TokenStream> {
    let struct_name = &input.ident;
    let operations_meta = input.get_metadata()?;
    let operation_properties = get_operation_properties(operations_meta)?;

    let current_crate = syn::Ident::new("crate", Span::call_site());

    let trait_derive = operation_properties
        .clone()
        .flows
        .into_iter()
        .map(|derive| {
            let fns = operation_properties
                .operations
                .iter()
                .map(|conversion| conversion.to_function(derive));
            derive.to_operation(fns, struct_name)
        })
        .collect::<Vec<_>>();

    let ref_trait_derive = operation_properties
        .flows
        .into_iter()
        .map(|derive| {
            let fns = operation_properties
                .operations
                .iter()
                .map(|conversion| conversion.to_ref_function(derive));
            derive.to_ref_operation(fns, struct_name)
        })
        .collect::<Vec<_>>();

    let trait_derive = quote! {
            #(#ref_trait_derive)* #(#trait_derive)*
    };

    let output = quote! {
        const _: () = {
                use #current_crate::core::errors::RouterResult;
                use #current_crate::core::payments::{PaymentData,operations::{
                    ValidateRequest,
                    PostUpdateTracker,
                    GetTracker,
                    UpdateTracker,
                }};
                use #current_crate::types::{
                    SetupMandateRequestData,
                    PaymentsSyncData,
                    PaymentsCaptureData,
                    PaymentsCancelData,
                    PaymentsApproveData,
                    PaymentsRejectData,
                    PaymentsAuthorizeData,
                    PaymentsSessionData,
                    CompleteAuthorizeData,
                    PaymentsIncrementalAuthorizationData,
                    SdkPaymentsSessionUpdateData,
                    PaymentsPostSessionTokensData,
                    PaymentsUpdateMetadataData,
                    PaymentsCancelPostCaptureData,

                    api::{
                        PaymentsCaptureRequest,
                        PaymentsCancelRequest,
                        PaymentsApproveRequest,
                        PaymentsRejectRequest,
                        PaymentsRetrieveRequest,
                        PaymentsRequest,
                        PaymentsStartRequest,
                        PaymentsSessionRequest,
                        VerifyRequest,
                        PaymentsDynamicTaxCalculationRequest,
                        PaymentsIncrementalAuthorizationRequest,
                        PaymentsPostSessionTokensRequest,
                        PaymentsUpdateMetadataRequest,
                        PaymentsCancelPostCaptureRequest,
                    }
                };
                #trait_derive
            };
    };
    Ok(proc_macro::TokenStream::from(output))
}


// File: crates/hyperswitch_connectors/src/connectors/nmi.rs
// Module: hyperswitch_connectors
// Public structs: 1

pub mod transformers;
use std::sync::LazyLock;

use api_models::webhooks::IncomingWebhookEvent;
use common_enums::{enums, CallConnectorAction, PaymentAction};
use common_utils::{
    crypto,
    errors::CustomResult,
    ext_traits::ByteSliceExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector},
};
use error_stack::{report, ResultExt};
use hex;
use hyperswitch_domain_models::{
    router_data::{AccessToken, ErrorResponse, RouterData},
    router_flow_types::{
        AccessTokenAuth, Authorize, Capture, CompleteAuthorize, Execute, PSync, PaymentMethodToken,
        PreProcessing, RSync, Session, SetupMandate, Void,
    },
    router_request_types::{
        AccessTokenRequestData, CompleteAuthorizeData, PaymentMethodTokenizationData,
        PaymentsAuthorizeData, PaymentsCancelData, PaymentsCaptureData, PaymentsPreProcessingData,
        PaymentsSessionData, PaymentsSyncData, RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCancelRouterData, PaymentsCaptureRouterData,
        PaymentsCompleteAuthorizeRouterData, PaymentsPreProcessingRouterData,
        PaymentsSyncRouterData, RefundsRouterData, SetupMandateRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorRedirectResponse,
        ConnectorSpecifications, ConnectorValidation,
    },
    configs::Connectors,
    errors::ConnectorError,
    events::connector_api_logs::ConnectorEvent,
    types::{
        PaymentsAuthorizeType, PaymentsCaptureType, PaymentsCompleteAuthorizeType,
        PaymentsPreProcessingType, PaymentsSyncType, PaymentsVoidType, RefundExecuteType,
        RefundSyncType, Response, SetupMandateType,
    },
    webhooks::{IncomingWebhook, IncomingWebhookRequestDetails},
};
use masking::Maskable;
use regex::Regex;
use transformers as nmi;

use crate::{
    types::ResponseRouterData,
    utils::{self, convert_amount, get_header_key_value},
};

#[derive(Clone)]
pub struct Nmi {
    amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
}

impl Nmi {
    pub const fn new() -> &'static Self {
        &Self {
            amount_converter: &FloatMajorUnitForConnector,
        }
    }
}

impl api::Payment for Nmi {}
impl api::PaymentSession for Nmi {}
impl api::ConnectorAccessToken for Nmi {}
impl api::MandateSetup for Nmi {}
impl api::PaymentAuthorize for Nmi {}
impl api::PaymentSync for Nmi {}
impl api::PaymentCapture for Nmi {}
impl api::PaymentVoid for Nmi {}
impl api::Refund for Nmi {}
impl api::RefundExecute for Nmi {}
impl api::RefundSync for Nmi {}
impl api::PaymentToken for Nmi {}

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Nmi
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        _req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        Ok(vec![(
            "Content-Type".to_string(),
            "application/x-www-form-urlencoded".to_string().into(),
        )])
    }
}

impl ConnectorCommon for Nmi {
    fn id(&self) -> &'static str {
        "nmi"
    }

    fn get_currency_unit(&self) -> api::CurrencyUnit {
        api::CurrencyUnit::Base
    }

    fn base_url<'a>(&self, connectors: &'a Connectors) -> &'a str {
        connectors.nmi.base_url.as_ref()
    }

    fn build_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        let response: nmi::StandardResponse = res
            .response
            .parse_struct("StandardResponse")
            .change_context(ConnectorError::ResponseDeserializationFailed)?;

        event_builder.map(|i| i.set_error_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        Ok(ErrorResponse {
            message: response.responsetext.to_owned(),
            status_code: res.status_code,
            reason: Some(response.responsetext),
            code: response.response_code,
            attempt_status: None,
            connector_transaction_id: Some(response.transactionid),
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Nmi {
    fn validate_psync_reference_id(
        &self,
        _data: &PaymentsSyncData,
        _is_three_ds: bool,
        _status: enums::AttemptStatus,
        _connector_meta_data: Option<common_utils::pii::SecretSerdeValue>,
    ) -> CustomResult<(), ConnectorError> {
        // in case we dont have transaction id, we can make psync using attempt id
        Ok(())
    }

    fn validate_mandate_payment(
        &self,
        pm_type: Option<enums::PaymentMethodType>,
        pm_data: hyperswitch_domain_models::payment_method_data::PaymentMethodData,
    ) -> CustomResult<(), ConnectorError> {
        let mandate_supported_pmd =
            std::collections::HashSet::from([utils::PaymentMethodDataType::Card]);
        utils::is_mandate_supported(pm_data, pm_type, mandate_supported_pmd, self.id())
    }
}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Nmi
{
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Nmi {}

impl ConnectorIntegration<AccessTokenAuth, AccessTokenRequestData, AccessToken> for Nmi {}

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &SetupMandateRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &SetupMandateRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/transact.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &SetupMandateRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let connector_req = nmi::NmiValidateRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &SetupMandateRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&SetupMandateType::get_url(self, req, connectors)?)
                .headers(SetupMandateType::get_headers(self, req, connectors)?)
                .set_body(SetupMandateType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &SetupMandateRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<SetupMandateRouterData, ConnectorError> {
        let response: nmi::StandardResponse = serde_urlencoded::from_bytes(&res.response)
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl api::PaymentsPreProcessing for Nmi {}

impl ConnectorIntegration<PreProcessing, PaymentsPreProcessingData, PaymentsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &PaymentsPreProcessingRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }

    fn get_url(
        &self,
        _req: &PaymentsPreProcessingRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/transact.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsPreProcessingRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let connector_req = nmi::NmiVaultRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsPreProcessingRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        let req = Some(
            RequestBuilder::new()
                .method(Method::Post)
                .attach_default_headers()
                .headers(PaymentsPreProcessingType::get_headers(
                    self, req, connectors,
                )?)
                .url(&PaymentsPreProcessingType::get_url(self, req, connectors)?)
                .set_body(PaymentsPreProcessingType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        );
        Ok(req)
    }

    fn handle_response(
        &self,
        data: &PaymentsPreProcessingRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsPreProcessingRouterData, ConnectorError> {
        let response: nmi::NmiVaultResponse = serde_urlencoded::from_bytes(&res.response)
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/transact.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;
        let connector_router_data = nmi::NmiRouterData::from((amount, req));
        let connector_req = nmi::NmiPaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsAuthorizeType::get_url(self, req, connectors)?)
                .headers(PaymentsAuthorizeType::get_headers(self, req, connectors)?)
                .set_body(PaymentsAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsAuthorizeRouterData, ConnectorError> {
        let response: nmi::StandardResponse = serde_urlencoded::from_bytes(&res.response)
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl api::PaymentsCompleteAuthorize for Nmi {}

impl ConnectorIntegration<CompleteAuthorize, CompleteAuthorizeData, PaymentsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &PaymentsCompleteAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }
    fn get_content_type(&self) -> &'static str {
        self.common_get_content_type()
    }
    fn get_url(
        &self,
        _req: &PaymentsCompleteAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/transact.php", self.base_url(connectors)))
    }
    fn get_request_body(
        &self,
        req: &PaymentsCompleteAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount,
            req.request.currency,
        )?;
        let connector_router_data = nmi::NmiRouterData::from((amount, req));
        let connector_req = nmi::NmiCompleteRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }
    fn build_request(
        &self,
        req: &PaymentsCompleteAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsCompleteAuthorizeType::get_url(
                    self, req, connectors,
                )?)
                .attach_default_headers()
                .headers(PaymentsCompleteAuthorizeType::get_headers(
                    self, req, connectors,
                )?)
                .set_body(PaymentsCompleteAuthorizeType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCompleteAuthorizeRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCompleteAuthorizeRouterData, ConnectorError> {
        let response: nmi::NmiCompleteResponse = serde_urlencoded::from_bytes(&res.response)
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/query.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let connector_req = nmi::NmiSyncRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsSyncRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsSyncType::get_url(self, req, connectors)?)
                .headers(PaymentsSyncType::get_headers(self, req, connectors)?)
                .set_body(PaymentsSyncType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, ConnectorError> {
        let response = nmi::SyncResponse::try_from(res.response.to_vec())?;

        event_builder.map(|i| i.set_response_body(&response));

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/transact.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let amount = convert_amount(
            self.amount_converter,
            req.request.minor_amount_to_capture,
            req.request.currency,
        )?;
        let connector_router_data = nmi::NmiRouterData::from((amount, req));
        let connector_req = nmi::NmiCaptureRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsCaptureRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsCaptureType::get_url(self, req, connectors)?)
                .headers(PaymentsCaptureType::get_headers(self, req, connectors)?)
                .set_body(PaymentsCaptureType::get_request_body(
                    self, req, connectors,
                )?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCaptureRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCaptureRouterData, ConnectorError> {
        let response: nmi::StandardResponse = serde_urlencoded::from_bytes(&res.response)
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/transact.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsCancelRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let connector_req = nmi::NmiCancelRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsCancelRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&PaymentsVoidType::get_url(self, req, connectors)?)
                .headers(PaymentsVoidType::get_headers(self, req, connectors)?)
                .set_body(PaymentsVoidType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &PaymentsCancelRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsCancelRouterData, ConnectorError> {
        let response: nmi::StandardResponse = serde_urlencoded::from_bytes(&res.response)
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/transact.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let refund_amount = convert_amount(
            self.amount_converter,
            req.request.minor_refund_amount,
            req.request.currency,
        )?;

        let connector_router_data = nmi::NmiRouterData::from((refund_amount, req));
        let connector_req = nmi::NmiRefundRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<Execute>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&RefundExecuteType::get_url(self, req, connectors)?)
                .headers(RefundExecuteType::get_headers(self, req, connectors)?)
                .set_body(RefundExecuteType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<Execute>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<Execute>, ConnectorError> {
        let response: nmi::StandardResponse = serde_urlencoded::from_bytes(&res.response)
            .change_context(ConnectorError::ResponseDeserializationFailed)?;
        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);
        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Nmi {
    fn get_headers(
        &self,
        req: &RefundsRouterData<RSync>,
        connectors: &Connectors,
    ) -> CustomResult<Vec<(String, Maskable<String>)>, ConnectorError> {
        self.build_headers(req, connectors)
    }

    fn get_url(
        &self,
        _req: &RefundsRouterData<RSync>,
        connectors: &Connectors,
    ) -> CustomResult<String, ConnectorError> {
        Ok(format!("{}api/query.php", self.base_url(connectors)))
    }

    fn get_request_body(
        &self,
        req: &RefundsRouterData<RSync>,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, ConnectorError> {
        let connector_req = nmi::NmiSyncRequest::try_from(req)?;
        Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &RefundsRouterData<RSync>,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, ConnectorError> {
        Ok(Some(
            RequestBuilder::new()
                .method(Method::Post)
                .url(&RefundSyncType::get_url(self, req, connectors)?)
                .headers(RefundSyncType::get_headers(self, req, connectors)?)
                .set_body(RefundSyncType::get_request_body(self, req, connectors)?)
                .build(),
        ))
    }

    fn handle_response(
        &self,
        data: &RefundsRouterData<RSync>,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<RefundsRouterData<RSync>, ConnectorError> {
        let response = nmi::NmiRefundSyncResponse::try_from(res.response.to_vec())?;

        event_builder.map(|i| i.set_response_body(&response));
        router_env::logger::info!(connector_response=?response);

        RouterData::try_from(ResponseRouterData {
            response,
            data: data.clone(),
            http_code: res.status_code,
        })
    }

    fn get_error_response(
        &self,
        res: Response,
        event_builder: Option<&mut ConnectorEvent>,
    ) -> CustomResult<ErrorResponse, ConnectorError> {
        self.build_error_response(res, event_builder)
    }
}

#[async_trait::async_trait]
impl IncomingWebhook for Nmi {
    fn get_webhook_source_verification_algorithm(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn crypto::VerifySignature + Send>, ConnectorError> {
        Ok(Box::new(crypto::HmacSha256))
    }

    fn get_webhook_source_verification_signature(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        let sig_header = get_header_key_value("webhook-signature", request.headers)?;

        let regex_pattern = r"t=(.*),s=(.*)";

        if let Some(captures) = Regex::new(regex_pattern)
            .change_context(ConnectorError::WebhookSignatureNotFound)?
            .captures(sig_header)
        {
            let signature = captures
                .get(2)
                .ok_or(ConnectorError::WebhookSignatureNotFound)?
                .as_str();

            // Decode hex signature to bytes
            hex::decode(signature).change_context(ConnectorError::WebhookSignatureNotFound)
        } else {
            Err(report!(ConnectorError::WebhookSignatureNotFound))
        }
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, ConnectorError> {
        let sig_header = get_header_key_value("webhook-signature", request.headers)?;

        let regex_pattern = r"t=(.*),s=(.*)";

        if let Some(captures) = Regex::new(regex_pattern)
            .change_context(ConnectorError::WebhookSignatureNotFound)?
            .captures(sig_header)
        {
            let nonce = captures
                .get(1)
                .ok_or(ConnectorError::WebhookSignatureNotFound)?
                .as_str();

            let message = format!("{}.{}", nonce, String::from_utf8_lossy(request.body));

            Ok(message.into_bytes())
        } else {
            Err(report!(ConnectorError::WebhookSignatureNotFound))
        }
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, ConnectorError> {
        let reference_body: nmi::NmiWebhookObjectReference = request
            .body
            .parse_struct("nmi NmiWebhookObjectReference")
            .change_context(ConnectorError::WebhookResourceObjectNotFound)?;

        let object_reference_id = match reference_body.event_body.action.action_type {
            nmi::NmiActionType::Sale => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Auth => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Capture => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Void => api_models::webhooks::ObjectReferenceId::PaymentId(
                api_models::payments::PaymentIdType::PaymentAttemptId(
                    reference_body.event_body.order_id,
                ),
            ),
            nmi::NmiActionType::Refund => api_models::webhooks::ObjectReferenceId::RefundId(
                api_models::webhooks::RefundIdType::RefundId(reference_body.event_body.order_id),
            ),
            _ => Err(ConnectorError::WebhooksNotImplemented)?,
        };

        Ok(object_reference_id)
    }

    fn get_webhook_event_type(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, ConnectorError> {
        let event_type_body: nmi::NmiWebhookEventBody = request
            .body
            .parse_struct("nmi NmiWebhookEventType")
            .change_context(ConnectorError::WebhookResourceObjectNotFound)?;

        Ok(transformers::get_nmi_webhook_event(
            event_type_body.event_type,
        ))
    }

    fn get_webhook_resource_object(
        &self,
        request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, ConnectorError> {
        let webhook_body: nmi::NmiWebhookBody = request
            .body
            .parse_struct("nmi NmiWebhookBody")
            .change_context(ConnectorError::WebhookResourceObjectNotFound)?;

        match webhook_body.event_body.action.action_type {
            nmi::NmiActionType::Sale
            | nmi::NmiActionType::Auth
            | nmi::NmiActionType::Capture
            | nmi::NmiActionType::Void
            | nmi::NmiActionType::Credit => {
                Ok(Box::new(nmi::SyncResponse::try_from(&webhook_body)?))
            }
            nmi::NmiActionType::Refund => Ok(Box::new(webhook_body)),
        }
    }
}

impl ConnectorRedirectResponse for Nmi {
    fn get_flow_type(
        &self,
        _query_params: &str,
        json_payload: Option<serde_json::Value>,
        action: PaymentAction,
    ) -> CustomResult<CallConnectorAction, ConnectorError> {
        match action {
            PaymentAction::CompleteAuthorize => {
                let payload_data = json_payload.ok_or(ConnectorError::MissingRequiredField {
                    field_name: "connector_metadata",
                })?;

                let redirect_res: nmi::NmiRedirectResponse = serde_json::from_value(payload_data)
                    .change_context(
                    ConnectorError::MissingConnectorRedirectionPayload {
                        field_name: "redirect_res",
                    },
                )?;

                match redirect_res {
                    transformers::NmiRedirectResponse::NmiRedirectResponseData(_) => {
                        Ok(CallConnectorAction::Trigger)
                    }
                    transformers::NmiRedirectResponse::NmiErrorResponseData(error_res) => {
                        Ok(CallConnectorAction::StatusUpdate {
                            status: enums::AttemptStatus::Failure,
                            error_code: Some(error_res.code),
                            error_message: Some(error_res.message),
                        })
                    }
                }
            }
            PaymentAction::PSync | PaymentAction::PaymentAuthenticateCompleteAuthorize => {
                Ok(CallConnectorAction::Trigger)
            }
        }
    }
}

static NMI_SUPPORTED_PAYMENT_METHODS: LazyLock<SupportedPaymentMethods> = LazyLock::new(|| {
    let supported_capture_methods = vec![
        enums::CaptureMethod::Automatic,
        enums::CaptureMethod::Manual,
        enums::CaptureMethod::SequentialAutomatic,
    ];

    let supported_card_network = vec![
        common_enums::CardNetwork::Mastercard,
        common_enums::CardNetwork::Visa,
        common_enums::CardNetwork::Interac,
        common_enums::CardNetwork::AmericanExpress,
        common_enums::CardNetwork::JCB,
        common_enums::CardNetwork::DinersClub,
        common_enums::CardNetwork::Discover,
        common_enums::CardNetwork::CartesBancaires,
        common_enums::CardNetwork::UnionPay,
        common_enums::CardNetwork::Maestro,
    ];

    let mut nmi_supported_payment_methods = SupportedPaymentMethods::new();

    nmi_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Credit,
        PaymentMethodDetails {
            mandates: enums::FeatureStatus::Supported,
            refunds: enums::FeatureStatus::Supported,
            supported_capture_methods: supported_capture_methods.clone(),
            specific_features: Some(
                api_models::feature_matrix::PaymentMethodSpecificFeatures::Card({
                    api_models::feature_matrix::CardSpecificFeatures {
                        three_ds: common_enums::FeatureStatus::Supported,
                        no_three_ds: common_enums::FeatureStatus::Supported,
                        supported_card_networks: supported_card_network.clone(),
                    }
                }),
            ),
        },
    );

    nmi_supported_payment_methods.add(
        enums::PaymentMethod::Card,
        enums::PaymentMethodType::Debit,


// Function: track_response_status_code
// File: crates/router/src/routes/metrics/request.rs
// Module: router

pub fn track_response_status_code<Q>(response: &ApplicationResponse<Q>) -> i64


// Struct: Handler
// File: crates/drainer/src/handler.rs
// Module: drainer
// Implementations: 2
// Traits: std::ops::Deref

pub struct Handler


// Implementation: impl IncomingWebhook for for Gocardless
// File: crates/hyperswitch_connectors/src/connectors/gocardless.rs
// Module: hyperswitch_connectors
// Methods: 6 total (0 public)

impl IncomingWebhook for for Gocardless


// Struct: ApplePayPaymentInformation
// File: crates/hyperswitch_connectors/src/connectors/bankofamerica/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct ApplePayPaymentInformation


// Implementation: impl Responder
// File: crates/router/src/routes/routing.rs
// Module: router
// Methods: 0 total (0 public)

impl Responder


// Struct: StaxTokenizeData
// File: crates/hyperswitch_connectors/src/connectors/stax/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct StaxTokenizeData


// Implementation: impl SerializableSecret for for i64
// File: crates/masking/src/serde.rs
// Module: masking
// Methods: 0 total (0 public)

impl SerializableSecret for for i64


// Struct: GenericLinkFormData
// File: crates/hyperswitch_domain_models/src/api.rs
// Module: hyperswitch_domain_models
// Implementations: 0

pub struct GenericLinkFormData


// Struct: RefundResponse
// File: crates/hyperswitch_connectors/src/connectors/shift4/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct RefundResponse


// File: crates/router/src/types/storage/callback_mapper.rs
// Module: router

pub use diesel_models::callback_mapper::CallbackMapper;


// Struct: PaypalIncrementalAuthRequest
// File: crates/hyperswitch_connectors/src/connectors/paypal/transformers.rs
// Module: hyperswitch_connectors
// Implementations: 0

pub struct PaypalIncrementalAuthRequest


// Implementation: impl api::PaymentSync for for Wise
// File: crates/hyperswitch_connectors/src/connectors/wise.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::PaymentSync for for Wise


// Implementation: impl api::Payment for for Vgs
// File: crates/hyperswitch_connectors/src/connectors/vgs.rs
// Module: hyperswitch_connectors
// Methods: 0 total (0 public)

impl api::Payment for for Vgs


// Struct: HashiCorpVault
// File: crates/external_services/src/hashicorp_vault/core.rs
// Module: external_services
// Implementations: 1

// Documentation: A struct representing a connection to HashiCorp Vault.

pub struct HashiCorpVault


// Function: generate_spt
// File: crates/router/src/types/domain/user/decision_manager.rs
// Module: router

pub fn generate_spt(
        self,
        state: &SessionState,
        next_flow: &NextFlow,
    ) -> UserResult<Secret<String>>