        let item = data.0;

        let mandate_metadata = item
            .request
            .mandate_id
            .as_ref()
            .and_then(|mandate_id| mandate_id.mandate_reference_id.as_ref())
            .and_then(|reference_id| match reference_id {
                payments::MandateReferenceId::ConnectorMandateId(mandate_data) => {
                    Some(mandate_data.get_mandate_metadata())
                }
                _ => None,
            });

        let (transfer_account_id, charge_type, application_fees) = if let Some(secret_value) =
            mandate_metadata.as_ref().and_then(|s| s.as_ref())
        {
            let json_value = secret_value.clone().expose();

            let parsed: Result<StripeSplitPaymentRequest, _> = serde_json::from_value(json_value);

            match parsed {
                Ok(data) => (
                    data.transfer_account_id,
                    data.charge_type,
                    data.application_fees,
                ),
                Err(_) => (None, None, None),
            }
        } else {
            (None, None, None)
        };

        let payment_method_token = match &item.request.split_payments {
            Some(SplitPaymentsRequest::StripeSplitPayment(_)) => {
                match item.payment_method_token.clone() {
                    Some(PaymentMethodToken::Token(secret)) => Some(secret),
                    _ => None,
                }
            }
            _ => None,
        };

        let amount = data.1;
        let order_id = item.connector_request_reference_id.clone();

        let shipping_address = if payment_method_token.is_some() {
            None
        } else {
            Some(StripeShippingAddress {
                city: item.get_optional_shipping_city(),
                country: item.get_optional_shipping_country(),
                line1: item.get_optional_shipping_line1(),
                line2: item.get_optional_shipping_line2(),
                zip: item.get_optional_shipping_zip(),
                state: item.get_optional_shipping_state(),
                name: item.get_optional_shipping_full_name(),
                phone: item.get_optional_shipping_phone_number(),
            })
        };

        let billing_address = if payment_method_token.is_some() {
            None
        } else {
            Some(StripeBillingAddress {
                city: item.get_optional_billing_city(),
                country: item.get_optional_billing_country(),
                address_line1: item.get_optional_billing_line1(),
                address_line2: item.get_optional_billing_line2(),
                zip_code: item.get_optional_billing_zip(),
                state: item.get_optional_billing_state(),
                name: item.get_optional_billing_full_name(),
                email: item.get_optional_billing_email(),
                phone: item.get_optional_billing_phone_number(),
            })
        };

        let mut payment_method_options = None;

        let (
            mut payment_data,
            payment_method,
            billing_address,
            payment_method_types,
            setup_future_usage,
        ) = if payment_method_token.is_some() {
            (None, None, StripeBillingAddress::default(), None, None)
        } else {
            match item
                .request
                .mandate_id
                .clone()
                .and_then(|mandate_ids| mandate_ids.mandate_reference_id)
            {
                Some(payments::MandateReferenceId::ConnectorMandateId(connector_mandate_ids)) => (
                    None,
                    connector_mandate_ids.get_connector_mandate_id(),
                    StripeBillingAddress::default(),
                    get_payment_method_type_for_saved_payment_method_payment(item)?,
                    None,
                ),
                Some(payments::MandateReferenceId::NetworkMandateId(network_transaction_id)) => {
                    payment_method_options = Some(StripePaymentMethodOptions::Card {
                        mandate_options: None,
                        network_transaction_id: None,
                        mit_exemption: Some(MitExemption {
                            network_transaction_id: Secret::new(network_transaction_id),
                        }),
                    });

                    let payment_data = match item.request.payment_method_data {
                        PaymentMethodData::CardDetailsForNetworkTransactionId(
                            ref card_details_for_network_transaction_id,
                        ) => StripePaymentMethodData::Card(StripeCardData {
                            payment_method_data_type: StripePaymentMethodType::Card,
                            payment_method_data_card_number:
                                card_details_for_network_transaction_id.card_number.clone(),
                            payment_method_data_card_exp_month:
                                card_details_for_network_transaction_id
                                    .card_exp_month
                                    .clone(),
                            payment_method_data_card_exp_year:
                                card_details_for_network_transaction_id
                                    .card_exp_year
                                    .clone(),
                            payment_method_data_card_cvc: None,
                            payment_method_auth_type: None,
                            payment_method_data_card_preferred_network:
                                card_details_for_network_transaction_id
                                    .card_network
                                    .clone()
                                    .and_then(get_stripe_card_network),
                            request_incremental_authorization: None,
                            request_extended_authorization: None,
                        }),
                        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(_)
                        | PaymentMethodData::Card(_) => Err(ConnectorError::NotSupported {
                            message: "Network tokenization for payment method".to_string(),
                            connector: "Stripe",
                        })?,
                    };

                    (
                        Some(payment_data),
                        None,
                        StripeBillingAddress::default(),
                        None,
                        None,
                    )
                }
                Some(payments::MandateReferenceId::NetworkTokenWithNTI(_)) | None => {
                    let (payment_method_data, payment_method_type, billing_address) =
                        create_stripe_payment_method(
                            &item.request.payment_method_data,
                            item.auth_type,
                            item.payment_method_token.clone(),
                            Some(
                                PaymentsAuthorizeRequestData::is_customer_initiated_mandate_payment(
                                    &item.request,
                                ),
                            ),
                            billing_address.ok_or_else(|| {
                                ConnectorError::MissingRequiredField {
                                    field_name: "billing_address",
                                }
                            })?,
                            item.request.request_incremental_authorization,
                            item.request.request_extended_authorization,
                        )?;

                    validate_shipping_address_against_payment_method(
                        &shipping_address,
                        payment_method_type.as_ref(),
                    )?;

                    (
                        Some(payment_method_data),
                        None,
                        billing_address,
                        payment_method_type,
                        item.request.setup_future_usage,
                    )
                }
            }
        };

        if payment_method_token.is_none() {
            payment_data = match item.request.payment_method_data {
                PaymentMethodData::Wallet(WalletData::ApplePay(_)) => {
                    let payment_method_token = item
                        .payment_method_token
                        .to_owned()
                        .get_required_value("payment_token")
                        .change_context(ConnectorError::InvalidWalletToken {
                            wallet_name: "Apple Pay".to_string(),
                        })?;

                    let payment_method_token = match payment_method_token {
                        PaymentMethodToken::Token(payment_method_token) => payment_method_token,
                        PaymentMethodToken::ApplePayDecrypt(_) => {
                            Err(ConnectorError::InvalidWalletToken {
                                wallet_name: "Apple Pay".to_string(),
                            })?
                        }
                        PaymentMethodToken::PazeDecrypt(_) => {
                            Err(crate::unimplemented_payment_method!("Paze", "Stripe"))?
                        }
                        PaymentMethodToken::GooglePayDecrypt(_) => {
                            Err(crate::unimplemented_payment_method!("Google Pay", "Stripe"))?
                        }
                    };
                    Some(StripePaymentMethodData::Wallet(
                        StripeWallet::ApplepayPayment(ApplepayPayment {
                            token: payment_method_token,
                            payment_method_types: StripePaymentMethodType::Card,
                        }),
                    ))
                }
                _ => payment_data,
            }
        } else {
            payment_data = None
        };

        let setup_mandate_details = item
            .request
            .setup_mandate_details
            .as_ref()
            .and_then(|mandate_details| {
                mandate_details
                    .customer_acceptance
                    .as_ref()
                    .map(|customer_acceptance| {
                        Ok::<_, error_stack::Report<ConnectorError>>(
                            match customer_acceptance.acceptance_type {
                                AcceptanceType::Online => {
                                    let online_mandate = customer_acceptance
                                        .online
                                        .clone()
                                        .get_required_value("online")
                                        .change_context(ConnectorError::MissingRequiredField {
                                            field_name: "online",
                                        })?;
                                    StripeMandateRequest {
                                        mandate_type: StripeMandateType::Online {
                                            ip_address: online_mandate
                                                .ip_address
                                                .get_required_value("ip_address")
                                                .change_context(
                                                    ConnectorError::MissingRequiredField {
                                                        field_name: "ip_address",
                                                    },
                                                )?,
                                            user_agent: online_mandate.user_agent,
                                        },
                                    }
                                }
                                AcceptanceType::Offline => StripeMandateRequest {
                                    mandate_type: StripeMandateType::Offline,
                                },
                            },
                        )
                    })
            })
            .transpose()?
            .or_else(|| {
                //stripe requires us to send mandate_data while making recurring payment through saved bank debit
                if payment_method.is_some() {
                    //check if payment is done through saved payment method
                    match &payment_method_types {
                        //check if payment method is bank debit
                        Some(
                            StripePaymentMethodType::Ach
                            | StripePaymentMethodType::Sepa
                            | StripePaymentMethodType::Becs
                            | StripePaymentMethodType::Bacs,
                        ) => Some(StripeMandateRequest {
                            mandate_type: StripeMandateType::Offline,
                        }),
                        _ => None,
                    }
                } else {
                    None
                }
            });

        let meta_data =
            get_transaction_metadata(item.request.metadata.clone().map(Into::into), order_id);

        // We pass browser_info only when payment_data exists.
        // Hence, we're pass Null during recurring payments as payment_method_data[type] is not passed
        let browser_info = if payment_data.is_some() && payment_method_token.is_none() {
            item.request
                .browser_info
                .clone()
                .map(StripeBrowserInformation::from)
        } else {
            None
        };

        let charges = match &item.request.split_payments {
            Some(SplitPaymentsRequest::StripeSplitPayment(stripe_split_payment)) => {
                match &stripe_split_payment.charge_type {
                    PaymentChargeType::Stripe(charge_type) => match charge_type {
                        StripeChargeType::Direct => Some(IntentCharges {
                            application_fee_amount: stripe_split_payment.application_fees,
                            destination_account_id: None,
                        }),
                        StripeChargeType::Destination => Some(IntentCharges {
                            application_fee_amount: stripe_split_payment.application_fees,
                            destination_account_id: Some(
                                stripe_split_payment.transfer_account_id.clone(),
                            ),
                        }),
                    },
                }
            }
            Some(SplitPaymentsRequest::AdyenSplitPayment(_))
            | Some(SplitPaymentsRequest::XenditSplitPayment(_))
            | None => None,
        };

        let charges_in = if charges.is_none() {
            match charge_type {
                Some(PaymentChargeType::Stripe(StripeChargeType::Direct)) => Some(IntentCharges {
                    application_fee_amount: application_fees, // default to 0 if None
                    destination_account_id: None,
                }),
                Some(PaymentChargeType::Stripe(StripeChargeType::Destination)) => {
                    Some(IntentCharges {
                        application_fee_amount: application_fees,
                        destination_account_id: transfer_account_id,
                    })
                }
                _ => None,
            }
        } else {
            charges
        };

        let pm = match (payment_method, payment_method_token.clone()) {
            (Some(method), _) => Some(Secret::new(method)),
            (None, Some(token)) => Some(token),
            (None, None) => None,
        };

        Ok(Self {
            amount,                                      //hopefully we don't loose some cents here
            currency: item.request.currency.to_string(), //we need to copy the value and not transfer ownership
            statement_descriptor_suffix: item.request.statement_descriptor_suffix.clone(),
            statement_descriptor: item.request.statement_descriptor.clone(),
            meta_data,
            return_url: item
                .request
                .router_return_url
                .clone()
                .unwrap_or_else(|| "https://juspay.in/".to_string()),
            confirm: true, // Stripe requires confirm to be true if return URL is present
            description: item.description.clone(),
            shipping: shipping_address,
            billing: billing_address,
            capture_method: StripeCaptureMethod::from(item.request.capture_method),
            payment_data,
            payment_method_options,
            payment_method: pm,
            customer: item.connector_customer.clone().map(Secret::new),
            setup_mandate_details,
            off_session: item.request.off_session,
            setup_future_usage: match (
                item.request.split_payments.as_ref(),
                item.request.setup_future_usage,
                item.request.customer_acceptance.as_ref(),
            ) {
                (Some(_), Some(usage), Some(_)) => Some(usage),
                _ => setup_future_usage,
            },

            payment_method_types,
            expand: Some(ExpandableObjects::LatestCharge),
            browser_info,
            charges: charges_in,
        })
    }
}

fn get_payment_method_type_for_saved_payment_method_payment(
    item: &PaymentsAuthorizeRouterData,
) -> Result<Option<StripePaymentMethodType>, error_stack::Report<ConnectorError>> {
    if item.payment_method == api_enums::PaymentMethod::Card {
        Ok(Some(StripePaymentMethodType::Card)) //stripe takes ["Card"] as default
    } else {
        let stripe_payment_method_type = match item.recurring_mandate_payment_data.clone() {
            Some(recurring_payment_method_data) => {
                match recurring_payment_method_data.payment_method_type {
                    Some(payment_method_type) => {
                        StripePaymentMethodType::try_from(payment_method_type)
                    }
                    None => Err(ConnectorError::MissingRequiredField {
                        field_name: "payment_method_type",
                    }
                    .into()),
                }
            }
            None => Err(ConnectorError::MissingRequiredField {
                field_name: "recurring_mandate_payment_data",
            }
            .into()),
        }?;
        match stripe_payment_method_type {
            //Stripe converts Ideal, Bancontact & Sofort Bank redirect methods to Sepa direct debit and attaches to the customer for future usage
            StripePaymentMethodType::Ideal
            | StripePaymentMethodType::Bancontact
            | StripePaymentMethodType::Sofort => Ok(Some(StripePaymentMethodType::Sepa)),
            _ => Ok(Some(stripe_payment_method_type)),
        }
    }
}

impl From<BrowserInformation> for StripeBrowserInformation {
    fn from(item: BrowserInformation) -> Self {
        Self {
            ip_address: item.ip_address.map(|ip| Secret::new(ip.to_string())),
            user_agent: item.user_agent,
        }
    }
}

impl TryFrom<&SetupMandateRouterData> for SetupIntentRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &SetupMandateRouterData) -> Result<Self, Self::Error> {
        //Only cards supported for mandates
        let pm_type = StripePaymentMethodType::Card;
        let payment_data = StripePaymentMethodData::try_from((item, item.auth_type, pm_type))?;

        let meta_data = Some(get_transaction_metadata(
            item.request.metadata.clone(),
            item.connector_request_reference_id.clone(),
        ));

        let browser_info = item
            .request
            .browser_info
            .clone()
            .map(StripeBrowserInformation::from);

        Ok(Self {
            confirm: true,
            payment_data,
            return_url: item.request.router_return_url.clone(),
            off_session: item.request.off_session,
            usage: item.request.setup_future_usage,
            payment_method_options: None,
            customer: item.connector_customer.to_owned().map(Secret::new),
            meta_data,
            payment_method_types: Some(pm_type),
            expand: Some(ExpandableObjects::LatestAttempt),
            browser_info,
        })
    }
}

impl TryFrom<&TokenizationRouterData> for TokenRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &TokenizationRouterData) -> Result<Self, Self::Error> {
        let billing_address = StripeBillingAddressCardToken {
            name: item.get_optional_billing_full_name(),
            email: item.get_optional_billing_email(),
            phone: item.get_optional_billing_phone_number(),
            address_line1: item.get_optional_billing_line1(),
            address_line2: item.get_optional_billing_line2(),
            city: item.get_optional_billing_city(),
            state: item.get_optional_billing_state(),
        };

        // Card flow for tokenization is handled separately because of API contact difference
        let request_payment_data = match &item.request.payment_method_data {
            PaymentMethodData::Card(card_details) => {
                StripePaymentMethodData::CardToken(StripeCardToken {
                    payment_method_type: Some(StripePaymentMethodType::Card),
                    token_card_number: card_details.card_number.clone(),
                    token_card_exp_month: card_details.card_exp_month.clone(),
                    token_card_exp_year: card_details.card_exp_year.clone(),
                    token_card_cvc: card_details.card_cvc.clone(),
                    billing: billing_address,
                })
            }
            _ => {
                create_stripe_payment_method(
                    &item.request.payment_method_data,
                    item.auth_type,
                    item.payment_method_token.clone(),
                    None,
                    StripeBillingAddress::default(),
                    false,
                    None,
                )?
                .0
            }
        };

        Ok(Self {
            token_data: request_payment_data,
        })
    }
}

impl TryFrom<&ConnectorCustomerRouterData> for CustomerRequest {
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(item: &ConnectorCustomerRouterData) -> Result<Self, Self::Error> {
        Ok(Self {
            description: item.request.description.to_owned(),
            email: item.request.email.to_owned(),
            phone: item.request.phone.to_owned(),
            name: item.request.name.to_owned(),
            source: item.request.preprocessing_id.to_owned().map(Secret::new),
        })
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct StripeSplitPaymentRequest {
    pub charge_type: Option<PaymentChargeType>,
    pub application_fees: Option<MinorUnit>,
    pub transfer_account_id: Option<String>,
}

impl TryFrom<&PaymentsAuthorizeRouterData> for StripeSplitPaymentRequest {
    type Error = error_stack::Report<ConnectorError>;

    fn try_from(item: &PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> {
        //extracting mandate metadata from CIT call if CIT call was a Split Payment
        let from_metadata = item
            .request
            .mandate_id
            .as_ref()
            .and_then(|mandate_id| mandate_id.mandate_reference_id.as_ref())
            .and_then(|reference_id| match reference_id {
                payments::MandateReferenceId::ConnectorMandateId(mandate_data) => {
                    mandate_data.get_mandate_metadata()
                }
                _ => None,
            })
            .and_then(|secret_value| {
                let json_value = secret_value.clone().expose();
                match serde_json::from_value::<Self>(json_value.clone()) {
                    Ok(val) => Some(val),
                    Err(err) => {
                        router_env::logger::info!(
                            "STRIPE: Picking merchant_account_id and merchant_config_currency from payments request: {:?}", err
                        );
                        None
                    }
                }
            });

        // If the Split Payment Request in MIT mismatches with the metadata from CIT, throw an error
        if from_metadata.is_some() && item.request.split_payments.is_some() {
            let mut mit_charge_type = None;
            let mut mit_application_fees = None;
            let mut mit_transfer_account_id = None;
            if let Some(SplitPaymentsRequest::StripeSplitPayment(stripe_split_payment)) =
                item.request.split_payments.as_ref()
            {
                mit_charge_type = Some(stripe_split_payment.charge_type.clone());
                mit_application_fees = stripe_split_payment.application_fees;
                mit_transfer_account_id = Some(stripe_split_payment.transfer_account_id.clone());
            }

            if mit_charge_type != from_metadata.as_ref().and_then(|m| m.charge_type.clone())
                || mit_application_fees != from_metadata.as_ref().and_then(|m| m.application_fees)
                || mit_transfer_account_id
                    != from_metadata
                        .as_ref()
                        .and_then(|m| m.transfer_account_id.clone())
            {
                let mismatched_fields = ["transfer_account_id", "application_fees", "charge_type"];

                let field_str = mismatched_fields.join(", ");
                return Err(error_stack::Report::from(
                    ConnectorError::MandatePaymentDataMismatch { fields: field_str },
                ));
            }
        }

        // If Mandate Metadata from CIT call has something, populate it
        let (charge_type, mut transfer_account_id, application_fees) =
            if let Some(ref metadata) = from_metadata {
                (
                    metadata.charge_type.clone(),
                    metadata.transfer_account_id.clone(),
                    metadata.application_fees,
                )
            } else {
                (None, None, None)
            };

        // If Charge Type is Destination, transfer_account_id need not be appended in headers
        if charge_type == Some(PaymentChargeType::Stripe(StripeChargeType::Destination)) {
            transfer_account_id = None;
        }
        Ok(Self {
            charge_type,
            transfer_account_id,
            application_fees,
        })
    }
}

#[derive(Debug, Serialize)]
pub struct StripeIncrementalAuthRequest {
    pub amount: MinorUnit,
}

#[derive(Clone, Default, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StripePaymentStatus {
    Succeeded,
    Failed,
    #[default]
    Processing,
    #[serde(rename = "requires_action")]
    RequiresCustomerAction,
    #[serde(rename = "requires_payment_method")]
    RequiresPaymentMethod,
    RequiresConfirmation,
    Canceled,
    RequiresCapture,
    Chargeable,
    Consumed,
    Pending,
}

impl From<StripePaymentStatus> for AttemptStatus {
    fn from(item: StripePaymentStatus) -> Self {
        match item {
            StripePaymentStatus::Succeeded => Self::Charged,
            StripePaymentStatus::Failed => Self::Failure,
            StripePaymentStatus::Processing => Self::Authorizing,
            StripePaymentStatus::RequiresCustomerAction => Self::AuthenticationPending,
            // Make the payment attempt status as failed
            StripePaymentStatus::RequiresPaymentMethod => Self::Failure,
            StripePaymentStatus::RequiresConfirmation => Self::ConfirmationAwaited,
            StripePaymentStatus::Canceled => Self::Voided,
            StripePaymentStatus::RequiresCapture => Self::Authorized,
            StripePaymentStatus::Chargeable => Self::Authorizing,
            StripePaymentStatus::Consumed => Self::Authorizing,
            StripePaymentStatus::Pending => Self::Pending,
        }
    }
}

#[derive(Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct PaymentIntentResponse {
    pub id: String,
    pub object: String,
    pub amount: MinorUnit,
    #[serde(default, deserialize_with = "deserialize_zero_minor_amount_as_none")]
    // stripe gives amount_captured as 0 for payment intents instead of 0
    pub amount_received: Option<MinorUnit>,
    pub amount_capturable: Option<MinorUnit>,
    pub currency: String,
    pub status: StripePaymentStatus,
    pub client_secret: Option<Secret<String>>,
    pub created: i32,
    pub customer: Option<Secret<String>>,
    pub payment_method: Option<Secret<String>>,
    pub description: Option<String>,
    pub statement_descriptor: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub metadata: StripeMetadata,
    pub next_action: Option<StripeNextActionResponse>,
    pub payment_method_options: Option<StripePaymentMethodOptions>,
    pub last_payment_error: Option<ErrorDetails>,
    pub latest_attempt: Option<LatestAttempt>, //need a merchant to test this
    pub latest_charge: Option<StripeChargeEnum>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct StripeSourceResponse {
    pub id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ach_credit_transfer: Option<AchCreditTransferResponse>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub multibanco: Option<MultibancoCreditTansferResponse>,
    pub receiver: AchReceiverDetails,
    pub status: StripePaymentStatus,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct AchCreditTransferResponse {
    pub account_number: Secret<String>,
    pub bank_name: Secret<String>,
    pub routing_number: Secret<String>,
    pub swift_code: Secret<String>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct MultibancoCreditTansferResponse {
    pub reference: Secret<String>,
    pub entity: Secret<String>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct AchReceiverDetails {
    pub amount_received: MinorUnit,
    pub amount_charged: MinorUnit,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct SepaAndBacsBankTransferInstructions {
    pub bacs_bank_instructions: Option<BacsFinancialDetails>,
    pub sepa_bank_instructions: Option<SepaFinancialDetails>,
    pub receiver: SepaAndBacsReceiver,
}

#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Serialize)]
pub struct QrCodeNextInstructions {
    pub image_data_url: Url,
    pub display_to_timestamp: Option<i64>,
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct SepaAndBacsReceiver {
    pub amount_received: MinorUnit,
    pub amount_remaining: MinorUnit,
}

#[derive(Debug, Default, Eq, PartialEq, Deserialize)]
pub struct PaymentSyncResponse {
    #[serde(flatten)]
    pub intent_fields: PaymentIntentResponse,
    pub last_payment_error: Option<ErrorDetails>,
}

impl Deref for PaymentSyncResponse {
    type Target = PaymentIntentResponse;

    fn deref(&self) -> &Self::Target {
        &self.intent_fields
    }
}

#[derive(Deserialize, Debug, Serialize)]
pub struct PaymentIntentSyncResponse {
    #[serde(flatten)]
    payment_intent_fields: PaymentIntentResponse,
    pub latest_charge: Option<StripeChargeEnum>,
}

#[derive(Debug, Eq, PartialEq, Deserialize, Clone, Serialize)]
#[serde(untagged)]
pub enum StripeChargeEnum {
    ChargeId(String),
    ChargeObject(StripeCharge),
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct StripeCharge {
    pub id: String,
    pub payment_method_details: Option<StripePaymentMethodDetailsResponse>,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct StripeBankRedirectDetails {
    #[serde(rename = "generated_sepa_debit")]
    attached_payment_method: Option<Secret<String>>,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct StripeCashappDetails {
    buyer_id: Option<String>,
    cashtag: Option<String>,
}

impl Deref for PaymentIntentSyncResponse {
    type Target = PaymentIntentResponse;

    fn deref(&self) -> &Self::Target {
        &self.payment_intent_fields
    }
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct StripeAdditionalCardDetails {
    checks: Option<Value>,
    three_d_secure: Option<Value>,
    network_transaction_id: Option<String>,
    extended_authorization: Option<StripeExtendedAuthorizationResponse>,
    #[serde(default, with = "common_utils::custom_serde::timestamp::option")]
    pub capture_before: Option<PrimitiveDateTime>,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
pub struct StripeExtendedAuthorizationResponse {
    status: Option<StripeExtendedAuthorizationStatus>,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum StripeExtendedAuthorizationStatus {
    Disabled,
    Enabled,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum StripePaymentMethodDetailsResponse {
    //only ideal and bancontact is supported by stripe for recurring payment in bank redirect
    Ideal {
        ideal: StripeBankRedirectDetails,
    },
    Bancontact {
        bancontact: StripeBankRedirectDetails,
    },

    //other payment method types supported by stripe. To avoid deserialization error.
    Blik,
    Eps,
    Fpx,
    Giropay,
    #[serde(rename = "p24")]
    Przelewy24,
    Card {
        card: StripeAdditionalCardDetails,
    },
    Cashapp {
        cashapp: StripeCashappDetails,
    },
    Klarna,
    Affirm,
    AfterpayClearpay,
    AmazonPay,
    ApplePay,
    #[serde(rename = "us_bank_account")]
    Ach,
    #[serde(rename = "sepa_debit")]
    Sepa,
    #[serde(rename = "au_becs_debit")]
    Becs,
    #[serde(rename = "bacs_debit")]
    Bacs,
    #[serde(rename = "wechat_pay")]
    Wechatpay,
    Alipay,
    CustomerBalance,
    RevolutPay,
}

pub struct AdditionalPaymentMethodDetails {
    pub payment_checks: Option<Value>,
    pub authentication_details: Option<Value>,
    pub extended_authorization: Option<StripeExtendedAuthorizationResponse>,
    pub capture_before: Option<PrimitiveDateTime>,
}

impl From<&AdditionalPaymentMethodDetails> for AdditionalPaymentMethodConnectorResponse {
    fn from(item: &AdditionalPaymentMethodDetails) -> Self {
        Self::Card {
            authentication_data: item.authentication_details.clone(),
            payment_checks: item.payment_checks.clone(),
            card_network: None,
            domestic_network: None,
        }
    }
}

impl From<&AdditionalPaymentMethodDetails> for ExtendedAuthorizationResponseData {
    fn from(item: &AdditionalPaymentMethodDetails) -> Self {
        Self {
            extended_authentication_applied: item.extended_authorization.as_ref().map(
                |extended_authorization| {
                    primitive_wrappers::ExtendedAuthorizationAppliedBool::from(matches!(
                        extended_authorization.status,
                        Some(StripeExtendedAuthorizationStatus::Enabled)
                    ))
                },
            ),
            capture_before: item.capture_before,
        }
    }
}

impl StripePaymentMethodDetailsResponse {
    pub fn get_additional_payment_method_data(&self) -> Option<AdditionalPaymentMethodDetails> {
        match self {
            Self::Card { card } => Some(AdditionalPaymentMethodDetails {
                payment_checks: card.checks.clone(),
                authentication_details: card.three_d_secure.clone(),
                extended_authorization: card.extended_authorization.clone(),
                capture_before: card.capture_before,
            }),
            Self::Ideal { .. }
            | Self::Bancontact { .. }
            | Self::Blik
            | Self::Eps
            | Self::Fpx
            | Self::Giropay
            | Self::Przelewy24
            | Self::Klarna
            | Self::Affirm
            | Self::AfterpayClearpay
            | Self::AmazonPay
            | Self::ApplePay
            | Self::Ach
            | Self::Sepa
            | Self::Becs
            | Self::Bacs
            | Self::Wechatpay
            | Self::Alipay
            | Self::CustomerBalance
            | Self::RevolutPay
            | Self::Cashapp { .. } => None,
        }
    }
}

#[derive(Deserialize)]
pub struct SetupIntentSyncResponse {
    #[serde(flatten)]
    setup_intent_fields: SetupIntentResponse,
}

impl Deref for SetupIntentSyncResponse {
    type Target = SetupIntentResponse;

    fn deref(&self) -> &Self::Target {
        &self.setup_intent_fields
    }
}

impl From<SetupIntentSyncResponse> for PaymentIntentSyncResponse {
    fn from(value: SetupIntentSyncResponse) -> Self {
        Self {
            payment_intent_fields: value.setup_intent_fields.into(),
            latest_charge: None,
        }
    }
}

impl From<SetupIntentResponse> for PaymentIntentResponse {
    fn from(value: SetupIntentResponse) -> Self {
        Self {
            id: value.id,
            object: value.object,
            status: value.status,
            client_secret: Some(value.client_secret),
            customer: value.customer,
            description: None,
            statement_descriptor: value.statement_descriptor,
            statement_descriptor_suffix: value.statement_descriptor_suffix,
            metadata: value.metadata,
            next_action: value.next_action,
            payment_method_options: value.payment_method_options,
            last_payment_error: value.last_setup_error,
            ..Default::default()
        }
    }
}

#[derive(Clone, Debug, Default, Eq, PartialEq, Deserialize, Serialize)]
pub struct SetupIntentResponse {
    pub id: String,
    pub object: String,
    pub status: StripePaymentStatus, // Change to SetupStatus
    pub client_secret: Secret<String>,
    pub customer: Option<Secret<String>>,
    pub payment_method: Option<String>,
    pub statement_descriptor: Option<String>,
    pub statement_descriptor_suffix: Option<String>,
    pub metadata: StripeMetadata,
    pub next_action: Option<StripeNextActionResponse>,
    pub payment_method_options: Option<StripePaymentMethodOptions>,
    pub latest_attempt: Option<LatestAttempt>,
    pub last_setup_error: Option<ErrorDetails>,
}

fn extract_payment_method_connector_response_from_latest_charge(
    stripe_charge_enum: &StripeChargeEnum,
) -> Option<ConnectorResponseData> {
    let additional_payment_method_details =
        if let StripeChargeEnum::ChargeObject(charge_object) = stripe_charge_enum {
            charge_object
                .payment_method_details
                .as_ref()
                .and_then(StripePaymentMethodDetailsResponse::get_additional_payment_method_data)
        } else {
            None
        };

    let additional_payment_method_data = additional_payment_method_details
        .as_ref()
        .map(AdditionalPaymentMethodConnectorResponse::from);
    let extended_authorization_data = additional_payment_method_details
        .as_ref()
        .map(ExtendedAuthorizationResponseData::from);

    if additional_payment_method_data.is_some() || extended_authorization_data.is_some() {
        Some(ConnectorResponseData::new(
            additional_payment_method_data,
            extended_authorization_data,
        ))
    } else {
        None
    }
}

fn extract_payment_method_connector_response_from_latest_attempt(
    stripe_latest_attempt: &LatestAttempt,
) -> Option<ConnectorResponseData> {
    if let LatestAttempt::PaymentIntentAttempt(intent_attempt) = stripe_latest_attempt {
        intent_attempt
            .payment_method_details
            .as_ref()
            .and_then(StripePaymentMethodDetailsResponse::get_additional_payment_method_data)
    } else {
        None
    }
    .as_ref()
    .map(AdditionalPaymentMethodConnectorResponse::from)
    .map(ConnectorResponseData::with_additional_payment_method_data)
}

impl<F, T> TryFrom<ResponseRouterData<F, PaymentIntentResponse, T, PaymentsResponseData>>
    for RouterData<F, T, PaymentsResponseData>
where
    T: SplitPaymentData + GetRequestIncrementalAuthorization,
{
    type Error = error_stack::Report<ConnectorError>;
    fn try_from(
        //
        item: ResponseRouterData<F, PaymentIntentResponse, T, PaymentsResponseData>,
    ) -> Result<Self, Self::Error> {
        let redirect_data = item.response.next_action.clone();
        let redirection_data = redirect_data
            .and_then(|redirection_data| redirection_data.get_url())
            .map(|redirection_url| RedirectForm::from((redirection_url, Method::Get)));

        let mandate_reference = item.response.payment_method.map(|payment_method_id| {
            // Implemented Save and re-use payment information for recurring charges
            // For more info: https://docs.stripe.com/recurring-payments#accept-recurring-payments
            // For backward compatibility payment_method_id & connector_mandate_id is being populated with the same value
            let connector_mandate_id = Some(payment_method_id.clone().expose());
            let payment_method_id = Some(payment_method_id.expose());

            let mandate_metadata: Option<Secret<Value>> =
                match item.data.request.get_split_payment_data() {
                    Some(SplitPaymentsRequest::StripeSplitPayment(stripe_split_data)) => {
                        Some(Secret::new(serde_json::json!({
                            "transfer_account_id": stripe_split_data.transfer_account_id,
                            "charge_type": stripe_split_data.charge_type,
                            "application_fees": stripe_split_data.application_fees,
                        })))
                    }
                    _ => None,
                };

            MandateReference {
                connector_mandate_id,
                payment_method_id,
                mandate_metadata,
                connector_mandate_request_reference_id: None,
            }
        });

        //Note: we might have to call retrieve_setup_intent to get the network_transaction_id in case its not sent in PaymentIntentResponse
        // Or we identify the mandate txns before hand and always call SetupIntent in case of mandate payment call
        let network_txn_id = match item.response.latest_charge.as_ref() {
            Some(StripeChargeEnum::ChargeObject(charge_object)) => charge_object
                .payment_method_details
                .as_ref()
                .and_then(|payment_method_details| match payment_method_details {
                    StripePaymentMethodDetailsResponse::Card { card } => {
                        card.network_transaction_id.clone()
                    }
                    _ => None,
                }),
            _ => None,
        };

        let connector_metadata =
            get_connector_metadata(item.response.next_action.as_ref(), item.response.amount)?;

        let status = AttemptStatus::from(item.response.status);


pub async fn get_payment_filters(
    state: SessionState,
    merchant_context: domain::MerchantContext,
    profile_id_list: Option<Vec<id_type::ProfileId>>,
) -> RouterResponse<api::PaymentListFiltersV2> {
    let merchant_connector_accounts = if let services::ApplicationResponse::Json(data) =
        super::admin::list_payment_connectors(
            state,
            merchant_context.get_merchant_account().get_id().to_owned(),
            profile_id_list,
        )
        .await?
    {
        data
    } else {
        return Err(errors::ApiErrorResponse::InternalServerError.into());
    };

    let mut connector_map: HashMap<String, Vec<MerchantConnectorInfo>> = HashMap::new();
    let mut payment_method_types_map: HashMap<
        enums::PaymentMethod,
        HashSet<enums::PaymentMethodType>,
    > = HashMap::new();

    // populate connector map
    merchant_connector_accounts
        .iter()
        .filter_map(|merchant_connector_account| {
            merchant_connector_account
                .connector_label
                .as_ref()
                .map(|label| {
                    let info = merchant_connector_account.to_merchant_connector_info(label);
                    (merchant_connector_account.get_connector_name(), info)
                })
        })
        .for_each(|(connector_name, info)| {
            connector_map
                .entry(connector_name.to_string())
                .or_default()
                .push(info);
        });

    // populate payment method type map
    merchant_connector_accounts
        .iter()
        .flat_map(|merchant_connector_account| {
            merchant_connector_account.payment_methods_enabled.as_ref()
        })
        .map(|payment_methods_enabled| {
            payment_methods_enabled
                .iter()
                .filter_map(|payment_method_enabled| {
                    payment_method_enabled
                        .get_payment_method_type()
                        .map(|types_vec| {
                            (
                                payment_method_enabled.get_payment_method(),
                                types_vec.clone(),
                            )
                        })
                })
        })
        .for_each(|payment_methods_enabled| {
            payment_methods_enabled.for_each(
                |(payment_method_option, payment_method_types_vec)| {
                    if let Some(payment_method) = payment_method_option {
                        payment_method_types_map
                            .entry(payment_method)
                            .or_default()
                            .extend(payment_method_types_vec.iter().filter_map(
                                |req_payment_method_types| {
                                    req_payment_method_types.get_payment_method_type()
                                },
                            ));
                    }
                },
            );
        });

    Ok(services::ApplicationResponse::Json(
        api::PaymentListFiltersV2 {
            connector: connector_map,
            currency: enums::Currency::iter().collect(),
            status: enums::IntentStatus::iter().collect(),
            payment_method: payment_method_types_map,
            authentication_type: enums::AuthenticationType::iter().collect(),
            card_network: enums::CardNetwork::iter().collect(),
            card_discovery: enums::CardDiscovery::iter().collect(),
        },
    ))
}


OpenAPI Block Path: components.schemas.BankRedirectBilling
{
  "type": "object",
  "required": [
    "billing_name",
    "email"
  ],
  "properties": {
    "billing_name": {
      "type": "string",
      "description": "The name for which billing is issued",
      "example": "John Doe"
    },
    "email": {
      "type": "string",
      "description": "The billing email for bank redirect",
      "example": "example@example.com"
    }
  }
}


pub async fn get_decrypted_vault_response_payload(
    jwekey: &settings::Jwekey,
    jwe_body: encryption::JweBody,
    decryption_scheme: settings::DecryptionScheme,
) -> CustomResult<String, errors::VaultError> {
    let public_key = jwekey.vault_encryption_key.peek().as_bytes();

    let private_key = jwekey.vault_private_key.peek().as_bytes();

    let jwt = get_dotted_jwe(jwe_body);
    let alg = match decryption_scheme {
        settings::DecryptionScheme::RsaOaep => jwe::RSA_OAEP,
        settings::DecryptionScheme::RsaOaep256 => jwe::RSA_OAEP_256,
    };

    let jwe_decrypted = encryption::decrypt_jwe(
        &jwt,
        encryption::KeyIdCheck::SkipKeyIdCheck,
        private_key,
        alg,
    )
    .await
    .change_context(errors::VaultError::SaveCardFailed)
    .attach_printable("Jwe Decryption failed for JweBody for vault")?;

    let jws = jwe_decrypted
        .parse_struct("JwsBody")
        .change_context(errors::VaultError::ResponseDeserializationFailed)?;
    let jws_body = get_dotted_jws(jws);

    encryption::verify_sign(jws_body, public_key)
        .change_context(errors::VaultError::SaveCardFailed)
        .attach_printable("Jws Decryption failed for JwsBody for vault")
}


OpenAPI Block Path: components.schemas.BancontactBankRedirectAdditionalData
{
  "type": "object",
  "properties": {
    "last4": {
      "type": "string",
      "description": "Last 4 digits of the card number",
      "example": "4242",
      "nullable": true
    },
    "card_exp_month": {
      "type": "string",
      "description": "The card's expiry month",
      "example": "12",
      "nullable": true
    },
    "card_exp_year": {
      "type": "string",
      "description": "The card's expiry year",
      "example": "24",
      "nullable": true
    },
    "card_holder_name": {
      "type": "string",
      "description": "The card holder's name",
      "example": "John Test",
      "nullable": true
    }
  }
}


pub async fn update_by_id(
        conn: &PgPooledConn,
        id: id_type::GlobalCustomerId,
        customer: CustomerUpdateInternal,
    ) -> StorageResult<Self> {
        match generics::generic_update_by_id::<<Self as HasTable>::Table, _, _, _>(
            conn,
            id.clone(),
            customer,
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_by_id::<<Self as HasTable>::Table, _, _>(conn, id).await
                }
                _ => Err(error),
            },
            result => result,
        }
    }


pub struct CardSecurityCode(StrongSecret<u16>);


Documentation: api-reference/v1/merchant-account/merchant-account--retrieve.mdx
# Type: Doc File

---
openapi: get /accounts/{account_id}
---


Documentation: api-reference/v1/event/events--list.mdx
# Type: Doc File

---
openapi: post /events/{merchant_id}
---


pub struct DummyConnectorRouterData<T> {
    pub amount: MinorUnit,
    pub router_data: T,
}


Web Documentation: Advanced Development Env Setup | Hyperswitch
# Type: Web Doc

These setup guides are meant for development. If you want a quick trial of Hyperswitch (without contributing), use
this guide
.
Setup Guides
Hyperswitch Backend
SDK (Frontend)
Control Center
Hyperswitch Components
Hyperswitch is built as a modular system, comprising three key components. Each component is designed for specific roles in the payment stack and has detailed development environment setup guides, linked above and also available in their individual repositories.
Hyperswitch App Server
:
The Hyperswitch App Server is the core engine for processing payments. It offers full support for various payment flows, including:
Core Operations
: Authorization, authentication, voids, captures, refunds, and chargeback handling.
Post-Payment Management
: Robust handling of disputes and reconciliations.
Routing Flexibility
:
Success-rate-based routing
Rule-based routing
Volume distribution
Fallback strategies
Intelligent retries using error-code-specific flows
Extensibility
: Connects with external fraud risk management (FRM) tools and authentication providers as part of the payment journey.
SDK (Frontend)
:
The Hyperswitch SDK delivers a seamless and unified checkout experience across platforms:
Platform Support
: Available for
Web
,
Android, and iOS
,.
Multi-Method Support
: Handles cards, wallets, BNPL, bank transfers, and more.
Flow Adaptability
: Supports the nuances of different PSPs’ payment flows.
Saved Payment Methods
: When integrated with the locker, the SDK automatically displays stored cards or other saved instruments for returning users.
Control Center
:
The Control Center is a no-code interface to manage and monitor your entire payment stack:
Workflow Management
: Configure smart routing, retries, 3DS invocation, fraud checks, and surcharge logic through a visual interface.
Operational Controls
:
Trigger and track refunds and chargebacks
View PSP-agnostic transaction logs for quick debugging
Insights and Analytics
: Access detailed reports and metrics on success rates, payment drop-offs, retry performance, and more.
7 months ago
Was this helpful?


pub async fn trigger_refund_outgoing_webhook(
    state: &SessionState,
    merchant_context: &domain::MerchantContext,
    refund: &diesel_models::Refund,
    profile_id: id_type::ProfileId,
) -> RouterResult<()> {
    let refund_status = refund.refund_status;

    let key_manager_state = &(state).into();
    let business_profile = state
        .store
        .find_business_profile_by_profile_id(
            key_manager_state,
            merchant_context.get_merchant_key_store(),
            &profile_id,
        )
        .await
        .to_not_found_response(errors::ApiErrorResponse::ProfileNotFound {
            id: profile_id.get_string_repr().to_owned(),
        })?;

    let should_trigger_webhook = business_profile
        .get_refund_webhook_statuses()
        .contains(&refund_status);

    if should_trigger_webhook {
        let event_type = refund_status.into();
        let refund_response: api_models::refunds::RefundResponse = refund.clone().foreign_into();
        let refund_id = refund_response.refund_id.clone();
        let cloned_state = state.clone();
        let cloned_merchant_context = merchant_context.clone();
        let primary_object_created_at = refund_response.created_at;
        if let Some(outgoing_event_type) = event_type {
            tokio::spawn(
                async move {
                    Box::pin(webhooks_core::create_event_and_trigger_outgoing_webhook(
                        cloned_state,
                        cloned_merchant_context,
                        business_profile,
                        outgoing_event_type,
                        diesel_models::enums::EventClass::Refunds,
                        refund_id.to_string(),
                        diesel_models::enums::EventObjectType::RefundDetails,
                        webhooks::OutgoingWebhookContent::RefundDetails(Box::new(refund_response)),
                        primary_object_created_at,
                    ))
                    .await
                }
                .in_current_span(),
            );
        } else {
            logger::warn!("Outgoing webhook not sent because of missing event type status mapping");
        };
    }
    Ok(())
}


impl IncomingWebhook for Datatrans {
    fn get_webhook_object_reference_id(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<ObjectReferenceId, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_event_type(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<IncomingWebhookEvent, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }

    fn get_webhook_resource_object(
        &self,
        _request: &IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        Err(report!(errors::ConnectorError::WebhooksNotImplemented))
    }
}


Documentation: api-reference/v1/gsm/gsm--delete.mdx
# Type: Doc File

---
openapi: post /gsm/delete
---


pub struct DlocalErrorResponse {
    pub code: i32,
    pub message: String,
    pub param: Option<String>,
}


impl api::RefundExecute for Taxjar {}


pub fn new() -> &'static Self {
        &Self {
            amount_converter: &FloatMajorUnitForConnector,
            amount_converter_webhooks: &StringMinorUnitForConnector,
        }
    }


File: crates/diesel_models/src/query/authentication.rs
 Public functions: 4

use diesel::{associations::HasTable, BoolExpressionMethods, ExpressionMethods};

use super::generics;
use crate::{
    authentication::{
        Authentication, AuthenticationNew, AuthenticationUpdate, AuthenticationUpdateInternal,
    },
    errors,
    schema::authentication::dsl,
    PgPooledConn, StorageResult,
};

impl AuthenticationNew {
    pub async fn insert(self, conn: &PgPooledConn) -> StorageResult<Authentication> {
        generics::generic_insert(conn, self).await
    }
}

impl Authentication {
    pub async fn update_by_merchant_id_authentication_id(
        conn: &PgPooledConn,
        merchant_id: common_utils::id_type::MerchantId,
        authentication_id: common_utils::id_type::AuthenticationId,
        authorization_update: AuthenticationUpdate,
    ) -> StorageResult<Self> {
        match generics::generic_update_with_unique_predicate_get_result::<
            <Self as HasTable>::Table,
            _,
            _,
            _,
        >(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::authentication_id.eq(authentication_id.to_owned())),
            AuthenticationUpdateInternal::from(authorization_update),
        )
        .await
        {
            Err(error) => match error.current_context() {
                errors::DatabaseError::NotFound => Err(error.attach_printable(
                    "Authentication with the given Authentication ID does not exist",
                )),
                errors::DatabaseError::NoFieldsToUpdate => {
                    generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
                        conn,
                        dsl::merchant_id
                            .eq(merchant_id.to_owned())
                            .and(dsl::authentication_id.eq(authentication_id.to_owned())),
                    )
                    .await
                }
                _ => Err(error),
            },
            result => result,
        }
    }

    pub async fn find_by_merchant_id_authentication_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        authentication_id: &common_utils::id_type::AuthenticationId,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::authentication_id.eq(authentication_id.to_owned())),
        )
        .await
    }

    pub async fn find_authentication_by_merchant_id_connector_authentication_id(
        conn: &PgPooledConn,
        merchant_id: &common_utils::id_type::MerchantId,
        connector_authentication_id: &str,
    ) -> StorageResult<Self> {
        generics::generic_find_one::<<Self as HasTable>::Table, _, _>(
            conn,
            dsl::merchant_id
                .eq(merchant_id.to_owned())
                .and(dsl::connector_authentication_id.eq(connector_authentication_id.to_owned())),
        )
        .await
    }
}


File: crates/hyperswitch_connectors/src/connectors/coinbase.rs
 Public functions: 1
 Public structs: 1

pub mod transformers;

use common_enums::enums;
use common_utils::{
    crypto,
    errors::CustomResult,
    ext_traits::ByteSliceExt,
    request::{Method, Request, RequestBuilder, RequestContent},
    types::{AmountConvertor, StringMajorUnit, StringMajorUnitForConnector},
};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
    connector_endpoints::Connectors,
    router_data::{AccessToken, ConnectorAuthType, ErrorResponse, RouterData},
    router_flow_types::{
        access_token_auth::AccessTokenAuth,
        payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
        refunds::{Execute, RSync},
    },
    router_request_types::{
        AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
        PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
        RefundsData, SetupMandateRequestData,
    },
    router_response_types::{
        ConnectorInfo, PaymentMethodDetails, PaymentsResponseData, RefundsResponseData,
        SupportedPaymentMethods, SupportedPaymentMethodsExt,
    },
    types::{
        PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
        RefundsRouterData,
    },
};
use hyperswitch_interfaces::{
    api::{
        self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorSpecifications,
        ConnectorValidation,
    },
    errors,
    events::connector_api_logs::ConnectorEvent,
    types::{PaymentsAuthorizeType, PaymentsSyncType, Response},
    webhooks,
};
use lazy_static::lazy_static;
use masking::Mask;
use transformers as coinbase;

use self::coinbase::CoinbaseWebhookDetails;
use crate::{
    constants::headers,
    types::ResponseRouterData,
    utils::{self, convert_amount},
};

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

impl Coinbase {
    pub fn new() -> &'static Self {
        &Self {
            amount_convertor: &StringMajorUnitForConnector,
        }
    }
}

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

impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Coinbase
where
    Self: ConnectorIntegration<Flow, Request, Response>,
{
    fn build_headers(
        &self,
        req: &RouterData<Flow, Request, Response>,
        _connectors: &Connectors,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let mut header = vec![
            (
                headers::CONTENT_TYPE.to_string(),
                self.common_get_content_type().to_string().into(),
            ),
            (
                headers::X_CC_VERSION.to_string(),
                "2018-03-22".to_string().into(),
            ),
        ];
        let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
        header.append(&mut api_key);
        Ok(header)
    }
}

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

    fn common_get_content_type(&self) -> &'static str {
        "application/json"
    }

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

    fn get_auth_header(
        &self,
        auth_type: &ConnectorAuthType,
    ) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
        let auth: coinbase::CoinbaseAuthType = coinbase::CoinbaseAuthType::try_from(auth_type)
            .change_context(errors::ConnectorError::FailedToObtainAuthType)?;
        Ok(vec![(
            headers::X_CC_API_KEY.to_string(),
            auth.api_key.into_masked(),
        )])
    }

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

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

        Ok(ErrorResponse {
            status_code: res.status_code,
            code: response.error.error_type,
            message: response.error.message,
            reason: response.error.code,
            attempt_status: None,
            connector_transaction_id: None,
            network_advice_code: None,
            network_decline_code: None,
            network_error_message: None,
            connector_metadata: None,
        })
    }
}

impl ConnectorValidation for Coinbase {}

impl ConnectorIntegration<PaymentMethodToken, PaymentMethodTokenizationData, PaymentsResponseData>
    for Coinbase
{
    // Not Implemented (R)
}

impl ConnectorIntegration<Session, PaymentsSessionData, PaymentsResponseData> for Coinbase {
    //TODO: implement sessions flow
}

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

impl ConnectorIntegration<SetupMandate, SetupMandateRequestData, PaymentsResponseData>
    for Coinbase
{
    fn build_request(
        &self,
        _req: &RouterData<SetupMandate, SetupMandateRequestData, PaymentsResponseData>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(
            errors::ConnectorError::NotImplemented("Setup Mandate flow for Coinbase".to_string())
                .into(),
        )
    }
}

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

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

    fn get_url(
        &self,
        _req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        Ok(format!("{}/charges", self.base_url(_connectors)))
    }

    fn get_request_body(
        &self,
        req: &PaymentsAuthorizeRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<RequestContent, errors::ConnectorError> {
        let amount = convert_amount(
            self.amount_convertor,
            req.request.minor_amount,
            req.request.currency,
        )?;

        let connector_router_data = coinbase::CoinbaseRouterData::from((amount, req));
        let connector_req = coinbase::CoinbasePaymentsRequest::try_from(&connector_router_data)?;
        Ok(RequestContent::Json(Box::new(connector_req)))
    }

    fn build_request(
        &self,
        req: &PaymentsAuthorizeRouterData,
        connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::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, errors::ConnectorError> {
        let response: coinbase::CoinbasePaymentsResponse = res
            .response
            .parse_struct("Coinbase PaymentsAuthorizeResponse")
            .change_context(errors::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,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

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

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

    fn get_url(
        &self,
        _req: &PaymentsSyncRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<String, errors::ConnectorError> {
        let connector_id = _req
            .request
            .connector_transaction_id
            .get_connector_transaction_id()
            .change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
        Ok(format!(
            "{}/charges/{}",
            self.base_url(_connectors),
            connector_id
        ))
    }

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

    fn handle_response(
        &self,
        data: &PaymentsSyncRouterData,
        event_builder: Option<&mut ConnectorEvent>,
        res: Response,
    ) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
        let response: coinbase::CoinbasePaymentsResponse = res
            .response
            .parse_struct("coinbase PaymentsSyncResponse")
            .change_context(errors::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,
        })
        .change_context(errors::ConnectorError::ResponseHandlingFailed)
    }

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

impl ConnectorIntegration<Capture, PaymentsCaptureData, PaymentsResponseData> for Coinbase {
    fn build_request(
        &self,
        _req: &PaymentsCaptureRouterData,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Capture".to_string(),
            connector: "Coinbase".to_string(),
        }
        .into())
    }
}

impl ConnectorIntegration<Void, PaymentsCancelData, PaymentsResponseData> for Coinbase {}

impl ConnectorIntegration<Execute, RefundsData, RefundsResponseData> for Coinbase {
    fn build_request(
        &self,
        _req: &RefundsRouterData<Execute>,
        _connectors: &Connectors,
    ) -> CustomResult<Option<Request>, errors::ConnectorError> {
        Err(errors::ConnectorError::FlowNotSupported {
            flow: "Refund".to_string(),
            connector: "Coinbase".to_string(),
        }
        .into())
    }
}

impl ConnectorIntegration<RSync, RefundsData, RefundsResponseData> for Coinbase {
    // default implementation of build_request method will be executed
}

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

    fn get_webhook_source_verification_signature(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let base64_signature =
            utils::get_header_key_value("X-CC-Webhook-Signature", request.headers)?;
        hex::decode(base64_signature)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)
    }

    fn get_webhook_source_verification_message(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
        _merchant_id: &common_utils::id_type::MerchantId,
        _connector_webhook_secrets: &api_models::webhooks::ConnectorWebhookSecrets,
    ) -> CustomResult<Vec<u8>, errors::ConnectorError> {
        let message = std::str::from_utf8(request.body)
            .change_context(errors::ConnectorError::WebhookSourceVerificationFailed)?;
        Ok(message.to_string().into_bytes())
    }

    fn get_webhook_object_reference_id(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::ObjectReferenceId, errors::ConnectorError> {
        let notif: CoinbaseWebhookDetails = request
            .body
            .parse_struct("CoinbaseWebhookDetails")
            .change_context(errors::ConnectorError::WebhookReferenceIdNotFound)?;
        Ok(api_models::webhooks::ObjectReferenceId::PaymentId(
            api_models::payments::PaymentIdType::ConnectorTransactionId(notif.event.data.id),
        ))
    }

    fn get_webhook_event_type(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<api_models::webhooks::IncomingWebhookEvent, errors::ConnectorError> {
        let notif: CoinbaseWebhookDetails = request
            .body
            .parse_struct("CoinbaseWebhookDetails")
            .change_context(errors::ConnectorError::WebhookEventTypeNotFound)?;
        match notif.event.event_type {
            coinbase::WebhookEventType::Confirmed | coinbase::WebhookEventType::Resolved => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentSuccess)
            }
            coinbase::WebhookEventType::Failed => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentActionRequired)
            }
            coinbase::WebhookEventType::Pending => {
                Ok(api_models::webhooks::IncomingWebhookEvent::PaymentIntentProcessing)
            }
            coinbase::WebhookEventType::Unknown | coinbase::WebhookEventType::Created => {
                Ok(api_models::webhooks::IncomingWebhookEvent::EventNotSupported)
            }
        }
    }

    fn get_webhook_resource_object(
        &self,
        request: &webhooks::IncomingWebhookRequestDetails<'_>,
    ) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
        let notif: CoinbaseWebhookDetails = request
            .body
            .parse_struct("CoinbaseWebhookDetails")
            .change_context(errors::ConnectorError::WebhookBodyDecodingFailed)?;

        Ok(Box::new(notif.event))
    }
}

lazy_static! {
    static ref COINBASE_CONNECTOR_INFO: ConnectorInfo = ConnectorInfo {
        display_name: "Coinbase",
        description:
            "Coinbase is a place for people and businesses to buy, sell, and manage crypto.",
        connector_type: enums::HyperswitchConnectorCategory::PaymentGateway,
        integration_status: enums::ConnectorIntegrationStatus::Beta,
    };
    static ref COINBASE_SUPPORTED_PAYMENT_METHODS: SupportedPaymentMethods = {
        let supported_capture_methods = vec![
            enums::CaptureMethod::Automatic,
            enums::CaptureMethod::Manual,
            enums::CaptureMethod::SequentialAutomatic,
        ];

        let mut coinbase_supported_payment_methods = SupportedPaymentMethods::new();

        coinbase_supported_payment_methods.add(
            enums::PaymentMethod::Crypto,
            enums::PaymentMethodType::CryptoCurrency,
            PaymentMethodDetails {
                mandates: common_enums::FeatureStatus::NotSupported,
                refunds: common_enums::FeatureStatus::NotSupported,
                supported_capture_methods: supported_capture_methods.clone(),
                specific_features: None,
            },
        );

        coinbase_supported_payment_methods
    };
    static ref COINBASE_SUPPORTED_WEBHOOK_FLOWS: Vec<enums::EventClass> =
        vec![enums::EventClass::Payments, enums::EventClass::Refunds,];
}

impl ConnectorSpecifications for Coinbase {
    fn get_connector_about(&self) -> Option<&'static ConnectorInfo> {
        Some(&*COINBASE_CONNECTOR_INFO)
    }

    fn get_supported_payment_methods(&self) -> Option<&'static SupportedPaymentMethods> {
        Some(&*COINBASE_SUPPORTED_PAYMENT_METHODS)
    }

    fn get_supported_webhook_flows(&self) -> Option<&'static [enums::EventClass]> {
        Some(&*COINBASE_SUPPORTED_WEBHOOK_FLOWS)
    }
}


pub struct FetchDisputesRequestData {
    pub created_from: time::PrimitiveDateTime,
    pub created_till: time::PrimitiveDateTime,
}


impl api::PaymentVoid for Santander {}


pub struct FiuuApplePayData {
    #[serde(rename = "TxnChannel")]
    txn_channel: TxnChannel,
    cc_month: Secret<String>,
    cc_year: Secret<String>,
    cc_token: CardNumber,
    eci: Option<String>,
    token_cryptogram: Secret<String>,
    token_type: FiuuTokenType,
    #[serde(rename = "non_3DS")]
    non_3ds: i32,
}


pub struct PayeezyAuthType {
    pub(super) api_key: Secret<String>,
    pub(super) api_secret: Secret<String>,
    pub(super) merchant_token: Secret<String>,
}


pub struct PaymentMethodSessionUpdateSavedPaymentMethod {
    /// The payment method id of the payment method to be updated
    #[schema(value_type = String, example = "12345_pm_01926c58bc6e77c09e809964e72af8c8")]
    pub payment_method_id: id_type::GlobalPaymentMethodId,

    /// The update request for the payment method update
    #[serde(flatten)]
    pub payment_method_update_request: PaymentMethodUpdate,
}


pub struct DashboardRequestPayload {
    pub connector: api_models::enums::Connector,
    pub payment_methods_enabled: Option<Vec<DashboardPaymentMethodPayload>>,
    pub metadata: Option<ApiModelMetaData>,
}


impl Tokenization {
    pub fn is_disabled(&self) -> bool {
        self.flag == common_enums::TokenizationFlag::Disabled
    }
}


OpenAPI Block Path: components.schemas.PaymentMethodMetaData
{
  "type": "object",
  "description": "Represents metadata about the payment method used in the 3DS decision rule.",
  "required": [
    "card_network"
  ],
  "properties": {
    "card_network": {
      "$ref": "#/components/schemas/CardNetwork"
    }
  }
}


pub fn get_current_date_time() -> CustomResult<String, errors::ConnectorError> {
        let format = format_description::parse(
            "[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] GMT",
        )
        .change_context(errors::ConnectorError::InvalidDateFormat)?;
        OffsetDateTime::now_utc()
            .format(&format)
            .change_context(errors::ConnectorError::InvalidDateFormat)
    }


pub async fn new_token(
        user_id: String,
        merchant_id: id_type::MerchantId,
        settings: &Settings,
        external_service_type: ExternalServiceType,
    ) -> UserResult<String> {
        let exp_duration = std::time::Duration::from_secs(consts::JWT_TOKEN_TIME_IN_SECS);
        let exp = jwt::generate_exp(exp_duration)?.as_secs();

        let token_payload = Self {
            user_id,
            merchant_id,
            exp,
            external_service_type,
        };
        jwt::generate_jwt(&token_payload, settings).await
    }


File: crates/api_models/src/pm_auth.rs
 Public structs: 6

use common_enums::{PaymentMethod, PaymentMethodType};
use common_utils::{
    events::{ApiEventMetric, ApiEventsType},
    id_type, impl_api_event_type,
};

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub struct LinkTokenCreateRequest {
    pub language: Option<String>, // optional language field to be passed
    pub client_secret: Option<String>, // client secret to be passed in req body
    pub payment_id: id_type::PaymentId, // payment_id to be passed in req body for redis pm_auth connector name fetch
    pub payment_method: PaymentMethod,  // payment_method to be used for filtering pm_auth connector
    pub payment_method_type: PaymentMethodType, // payment_method_type to be used for filtering pm_auth connector
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct LinkTokenCreateResponse {
    pub link_token: String, // link_token received in response
    pub connector: String,  // pm_auth connector name in response
}

#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ExchangeTokenCreateRequest {
    pub public_token: String,
    pub client_secret: Option<String>,
    pub payment_id: id_type::PaymentId,
    pub payment_method: PaymentMethod,
    pub payment_method_type: PaymentMethodType,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ExchangeTokenCreateResponse {
    pub access_token: String,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentMethodAuthConfig {
    pub enabled_payment_methods: Vec<PaymentMethodAuthConnectorChoice>,
}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct PaymentMethodAuthConnectorChoice {
    pub payment_method: PaymentMethod,
    pub payment_method_type: PaymentMethodType,
    pub connector_name: String,
    pub mca_id: id_type::MerchantConnectorAccountId,
}

impl_api_event_type!(
    Miscellaneous,
    (
        LinkTokenCreateRequest,
        LinkTokenCreateResponse,
        ExchangeTokenCreateRequest,
        ExchangeTokenCreateResponse
    )
);


impl api::Refund for Affirm {}


impl<T, A> EventContext<T, A>
where
    A: MessagingInterface<MessageClass = T>,
{
    /// Create a new event context.
    pub fn new(message_sink: A) -> Self {
        Self {
            message_sink: Arc::new(message_sink),
            metadata: HashMap::new(),
        }
    }

    /// Add metadata to the event context.
    #[track_caller]
    pub fn record_info<G: ErasedMaskSerialize, E: EventInfo<Data = G> + 'static>(
        &mut self,
        info: E,
    ) {
        match info.data().and_then(|i| {
            i.masked_serialize()
                .change_context(EventsError::SerializationError)
        }) {
            Ok(data) => {
                self.metadata.insert(info.key(), data);
            }
            Err(e) => {
                logger::error!("Error recording event info: {:?}", e);
            }
        }
    }

    /// Emit an event.
    pub fn try_emit<E: Event<EventType = T>>(&self, event: E) -> Result<(), EventsError> {
        EventBuilder {
            message_sink: self.message_sink.clone(),
            metadata: self.metadata.clone(),
            event,
        }
        .try_emit()
    }

    /// Emit an event.
    pub fn emit<D, E: Event<EventType = T, Data = D>>(&self, event: E) {
        EventBuilder {
            message_sink: self.message_sink.clone(),
            metadata: self.metadata.clone(),
            event,
        }
        .emit()
    }

    /// Create an event builder.
    pub fn event<D, E: Event<EventType = T, Data = D>>(
        &self,
        event: E,
    ) -> EventBuilder<T, A, E, D> {
        EventBuilder {
            message_sink: self.message_sink.clone(),
            metadata: self.metadata.clone(),
            event,
        }
    }
}


impl api::PaymentToken for Placetopay {}

impl api::PaymentToken for for Placetopay