mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +08:00 
			
		
		
		
	feat(connector): [ACI] implement Card Mandates for ACI (#1174)
This commit is contained in:
		| @ -5,6 +5,7 @@ use std::fmt::Debug; | |||||||
| use error_stack::{IntoReport, ResultExt}; | use error_stack::{IntoReport, ResultExt}; | ||||||
| use transformers as aci; | use transformers as aci; | ||||||
|  |  | ||||||
|  | use super::utils::PaymentsAuthorizeRequestData; | ||||||
| use crate::{ | use crate::{ | ||||||
|     configs::settings, |     configs::settings, | ||||||
|     core::errors::{self, CustomResult}, |     core::errors::{self, CustomResult}, | ||||||
| @ -245,10 +246,17 @@ impl | |||||||
|  |  | ||||||
|     fn get_url( |     fn get_url( | ||||||
|         &self, |         &self, | ||||||
|         _req: &types::PaymentsAuthorizeRouterData, |         req: &types::PaymentsAuthorizeRouterData, | ||||||
|         connectors: &settings::Connectors, |         connectors: &settings::Connectors, | ||||||
|     ) -> CustomResult<String, errors::ConnectorError> { |     ) -> CustomResult<String, errors::ConnectorError> { | ||||||
|         Ok(format!("{}{}", self.base_url(connectors), "v1/payments")) |         match req.request.connector_mandate_id() { | ||||||
|  |             Some(mandate_id) => Ok(format!( | ||||||
|  |                 "{}v1/registrations/{}/payments", | ||||||
|  |                 self.base_url(connectors), | ||||||
|  |                 mandate_id | ||||||
|  |             )), | ||||||
|  |             _ => Ok(format!("{}{}", self.base_url(connectors), "v1/payments")), | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fn get_request_body( |     fn get_request_body( | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
|  |  | ||||||
|  | use api_models::enums::BankNames; | ||||||
| use common_utils::pii::Email; | use common_utils::pii::Email; | ||||||
| use error_stack::report; | use error_stack::report; | ||||||
| use masking::Secret; | use masking::Secret; | ||||||
| @ -8,12 +9,14 @@ use serde::{Deserialize, Serialize}; | |||||||
|  |  | ||||||
| use super::result_codes::{FAILURE_CODES, PENDING_CODES, SUCCESSFUL_CODES}; | use super::result_codes::{FAILURE_CODES, PENDING_CODES, SUCCESSFUL_CODES}; | ||||||
| use crate::{ | use crate::{ | ||||||
|     connector::utils, |     connector::utils::{self, RouterData}, | ||||||
|     core::errors, |     core::errors, | ||||||
|     services, |     services, | ||||||
|     types::{self, api, storage::enums}, |     types::{self, api, storage::enums}, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | type Error = error_stack::Report<errors::ConnectorError>; | ||||||
|  |  | ||||||
| pub struct AciAuthType { | pub struct AciAuthType { | ||||||
|     pub api_key: String, |     pub api_key: String, | ||||||
|     pub entity_id: String, |     pub entity_id: String, | ||||||
| @ -36,12 +39,22 @@ impl TryFrom<&types::ConnectorAuthType> for AciAuthType { | |||||||
| #[derive(Debug, Serialize)] | #[derive(Debug, Serialize)] | ||||||
| #[serde(rename_all = "camelCase")] | #[serde(rename_all = "camelCase")] | ||||||
| pub struct AciPaymentsRequest { | pub struct AciPaymentsRequest { | ||||||
|  |     #[serde(flatten)] | ||||||
|  |     pub txn_details: TransactionDetails, | ||||||
|  |     #[serde(flatten)] | ||||||
|  |     pub payment_method: PaymentDetails, | ||||||
|  |     #[serde(flatten)] | ||||||
|  |     pub instruction: Option<Instruction>, | ||||||
|  |     pub shopper_result_url: Option<String>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, Serialize, Deserialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct TransactionDetails { | ||||||
|     pub entity_id: String, |     pub entity_id: String, | ||||||
|     pub amount: String, |     pub amount: String, | ||||||
|     pub currency: String, |     pub currency: String, | ||||||
|     pub payment_type: AciPaymentType, |     pub payment_type: AciPaymentType, | ||||||
|     #[serde(flatten)] |  | ||||||
|     pub payment_method: PaymentDetails, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Serialize)] | #[derive(Debug, Serialize)] | ||||||
| @ -59,6 +72,160 @@ pub enum PaymentDetails { | |||||||
|     BankRedirect(Box<BankRedirectionPMData>), |     BankRedirect(Box<BankRedirectionPMData>), | ||||||
|     Wallet(Box<WalletPMData>), |     Wallet(Box<WalletPMData>), | ||||||
|     Klarna, |     Klarna, | ||||||
|  |     Mandate, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl TryFrom<&api_models::payments::WalletData> for PaymentDetails { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from(wallet_data: &api_models::payments::WalletData) -> Result<Self, Self::Error> { | ||||||
|  |         let payment_data = match wallet_data { | ||||||
|  |             api_models::payments::WalletData::MbWayRedirect(data) => { | ||||||
|  |                 Self::Wallet(Box::new(WalletPMData { | ||||||
|  |                     payment_brand: PaymentBrand::Mbway, | ||||||
|  |                     account_id: Some(data.telephone_number.clone()), | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |             api_models::payments::WalletData::AliPayRedirect { .. } => { | ||||||
|  |                 Self::Wallet(Box::new(WalletPMData { | ||||||
|  |                     payment_brand: PaymentBrand::AliPay, | ||||||
|  |                     account_id: None, | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |             _ => Err(errors::ConnectorError::NotImplemented( | ||||||
|  |                 "Payment method".to_string(), | ||||||
|  |             ))?, | ||||||
|  |         }; | ||||||
|  |         Ok(payment_data) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl | ||||||
|  |     TryFrom<( | ||||||
|  |         &types::PaymentsAuthorizeRouterData, | ||||||
|  |         &api_models::payments::BankRedirectData, | ||||||
|  |     )> for PaymentDetails | ||||||
|  | { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from( | ||||||
|  |         value: ( | ||||||
|  |             &types::PaymentsAuthorizeRouterData, | ||||||
|  |             &api_models::payments::BankRedirectData, | ||||||
|  |         ), | ||||||
|  |     ) -> Result<Self, Self::Error> { | ||||||
|  |         let (item, bank_redirect_data) = value; | ||||||
|  |         let payment_data = match bank_redirect_data { | ||||||
|  |             api_models::payments::BankRedirectData::Eps { .. } => { | ||||||
|  |                 Self::BankRedirect(Box::new(BankRedirectionPMData { | ||||||
|  |                     payment_brand: PaymentBrand::Eps, | ||||||
|  |                     bank_account_country: Some(api_models::enums::CountryAlpha2::AT), | ||||||
|  |                     bank_account_bank_name: None, | ||||||
|  |                     bank_account_bic: None, | ||||||
|  |                     bank_account_iban: None, | ||||||
|  |                     billing_country: None, | ||||||
|  |                     merchant_customer_id: None, | ||||||
|  |                     merchant_transaction_id: None, | ||||||
|  |                     customer_email: None, | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |             api_models::payments::BankRedirectData::Giropay { | ||||||
|  |                 bank_account_bic, | ||||||
|  |                 bank_account_iban, | ||||||
|  |                 .. | ||||||
|  |             } => Self::BankRedirect(Box::new(BankRedirectionPMData { | ||||||
|  |                 payment_brand: PaymentBrand::Giropay, | ||||||
|  |                 bank_account_country: Some(api_models::enums::CountryAlpha2::DE), | ||||||
|  |                 bank_account_bank_name: None, | ||||||
|  |                 bank_account_bic: bank_account_bic.clone(), | ||||||
|  |                 bank_account_iban: bank_account_iban.clone(), | ||||||
|  |                 billing_country: None, | ||||||
|  |                 merchant_customer_id: None, | ||||||
|  |                 merchant_transaction_id: None, | ||||||
|  |                 customer_email: None, | ||||||
|  |             })), | ||||||
|  |             api_models::payments::BankRedirectData::Ideal { bank_name, .. } => { | ||||||
|  |                 Self::BankRedirect(Box::new(BankRedirectionPMData { | ||||||
|  |                     payment_brand: PaymentBrand::Ideal, | ||||||
|  |                     bank_account_country: Some(api_models::enums::CountryAlpha2::NL), | ||||||
|  |                     bank_account_bank_name: bank_name.to_owned(), | ||||||
|  |                     bank_account_bic: None, | ||||||
|  |                     bank_account_iban: None, | ||||||
|  |                     billing_country: None, | ||||||
|  |                     merchant_customer_id: None, | ||||||
|  |                     merchant_transaction_id: None, | ||||||
|  |                     customer_email: None, | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |             api_models::payments::BankRedirectData::Sofort { country, .. } => { | ||||||
|  |                 Self::BankRedirect(Box::new(BankRedirectionPMData { | ||||||
|  |                     payment_brand: PaymentBrand::Sofortueberweisung, | ||||||
|  |                     bank_account_country: Some(country.to_owned()), | ||||||
|  |                     bank_account_bank_name: None, | ||||||
|  |                     bank_account_bic: None, | ||||||
|  |                     bank_account_iban: None, | ||||||
|  |                     billing_country: None, | ||||||
|  |                     merchant_customer_id: None, | ||||||
|  |                     merchant_transaction_id: None, | ||||||
|  |                     customer_email: None, | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |             api_models::payments::BankRedirectData::Przelewy24 { | ||||||
|  |                 billing_details, .. | ||||||
|  |             } => Self::BankRedirect(Box::new(BankRedirectionPMData { | ||||||
|  |                 payment_brand: PaymentBrand::Przelewy, | ||||||
|  |                 bank_account_country: None, | ||||||
|  |                 bank_account_bank_name: None, | ||||||
|  |                 bank_account_bic: None, | ||||||
|  |                 bank_account_iban: None, | ||||||
|  |                 billing_country: None, | ||||||
|  |                 merchant_customer_id: None, | ||||||
|  |                 merchant_transaction_id: None, | ||||||
|  |                 customer_email: billing_details.email.to_owned(), | ||||||
|  |             })), | ||||||
|  |             api_models::payments::BankRedirectData::Interac { email, country } => { | ||||||
|  |                 Self::BankRedirect(Box::new(BankRedirectionPMData { | ||||||
|  |                     payment_brand: PaymentBrand::InteracOnline, | ||||||
|  |                     bank_account_country: Some(country.to_owned()), | ||||||
|  |                     bank_account_bank_name: None, | ||||||
|  |                     bank_account_bic: None, | ||||||
|  |                     bank_account_iban: None, | ||||||
|  |                     billing_country: None, | ||||||
|  |                     merchant_customer_id: None, | ||||||
|  |                     merchant_transaction_id: None, | ||||||
|  |                     customer_email: Some(email.to_owned()), | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |             api_models::payments::BankRedirectData::Trustly { country } => { | ||||||
|  |                 Self::BankRedirect(Box::new(BankRedirectionPMData { | ||||||
|  |                     payment_brand: PaymentBrand::Trustly, | ||||||
|  |                     bank_account_country: None, | ||||||
|  |                     bank_account_bank_name: None, | ||||||
|  |                     bank_account_bic: None, | ||||||
|  |                     bank_account_iban: None, | ||||||
|  |                     billing_country: Some(country.to_owned()), | ||||||
|  |                     merchant_customer_id: Some(Secret::new(item.get_customer_id()?)), | ||||||
|  |                     merchant_transaction_id: Some(Secret::new(item.payment_id.clone())), | ||||||
|  |                     customer_email: None, | ||||||
|  |                 })) | ||||||
|  |             } | ||||||
|  |             _ => Err(errors::ConnectorError::NotImplemented( | ||||||
|  |                 "Payment method".to_string(), | ||||||
|  |             ))?, | ||||||
|  |         }; | ||||||
|  |         Ok(payment_data) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl TryFrom<api_models::payments::Card> for PaymentDetails { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from(card_data: api_models::payments::Card) -> Result<Self, Self::Error> { | ||||||
|  |         Ok(Self::AciCard(Box::new(CardDetails { | ||||||
|  |             card_number: card_data.card_number, | ||||||
|  |             card_holder: card_data.card_holder_name, | ||||||
|  |             card_expiry_month: card_data.card_exp_month, | ||||||
|  |             card_expiry_year: card_data.card_exp_year, | ||||||
|  |             card_cvv: card_data.card_cvc, | ||||||
|  |         }))) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Serialize)] | #[derive(Debug, Clone, Serialize)] | ||||||
| @ -68,7 +235,7 @@ pub struct BankRedirectionPMData { | |||||||
|     #[serde(rename = "bankAccount.country")] |     #[serde(rename = "bankAccount.country")] | ||||||
|     bank_account_country: Option<api_models::enums::CountryAlpha2>, |     bank_account_country: Option<api_models::enums::CountryAlpha2>, | ||||||
|     #[serde(rename = "bankAccount.bankName")] |     #[serde(rename = "bankAccount.bankName")] | ||||||
|     bank_account_bank_name: Option<String>, |     bank_account_bank_name: Option<BankNames>, | ||||||
|     #[serde(rename = "bankAccount.bic")] |     #[serde(rename = "bankAccount.bic")] | ||||||
|     bank_account_bic: Option<Secret<String>>, |     bank_account_bic: Option<Secret<String>>, | ||||||
|     #[serde(rename = "bankAccount.iban")] |     #[serde(rename = "bankAccount.iban")] | ||||||
| @ -80,7 +247,6 @@ pub struct BankRedirectionPMData { | |||||||
|     #[serde(rename = "customer.merchantCustomerId")] |     #[serde(rename = "customer.merchantCustomerId")] | ||||||
|     merchant_customer_id: Option<Secret<String>>, |     merchant_customer_id: Option<Secret<String>>, | ||||||
|     merchant_transaction_id: Option<Secret<String>>, |     merchant_transaction_id: Option<Secret<String>>, | ||||||
|     shopper_result_url: Option<String>, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Serialize)] | #[derive(Debug, Clone, Serialize)] | ||||||
| @ -89,7 +255,6 @@ pub struct WalletPMData { | |||||||
|     payment_brand: PaymentBrand, |     payment_brand: PaymentBrand, | ||||||
|     #[serde(rename = "virtualAccount.accountId")] |     #[serde(rename = "virtualAccount.accountId")] | ||||||
|     account_id: Option<Secret<String>>, |     account_id: Option<Secret<String>>, | ||||||
|     shopper_result_url: Option<String>, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Serialize, Deserialize)] | #[derive(Debug, Clone, Serialize, Deserialize)] | ||||||
| @ -121,6 +286,43 @@ pub struct CardDetails { | |||||||
|     pub card_cvv: Secret<String>, |     pub card_cvv: Secret<String>, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "UPPERCASE")] | ||||||
|  | pub enum InstructionMode { | ||||||
|  |     Initial, | ||||||
|  |     Repeated, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "UPPERCASE")] | ||||||
|  | pub enum InstructionType { | ||||||
|  |     Unscheduled, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "UPPERCASE")] | ||||||
|  | pub enum InstructionSource { | ||||||
|  |     // Cardholder initiated transaction | ||||||
|  |     Cit, | ||||||
|  |     // Merchant initiated transaction | ||||||
|  |     Mit, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, Serialize)] | ||||||
|  | #[serde(rename_all = "camelCase")] | ||||||
|  | pub struct Instruction { | ||||||
|  |     #[serde(rename = "standingInstruction.mode")] | ||||||
|  |     mode: InstructionMode, | ||||||
|  |  | ||||||
|  |     #[serde(rename = "standingInstruction.type")] | ||||||
|  |     transaction_type: InstructionType, | ||||||
|  |  | ||||||
|  |     #[serde(rename = "standingInstruction.source")] | ||||||
|  |     source: InstructionSource, | ||||||
|  |  | ||||||
|  |     create_registration: Option<bool>, | ||||||
|  | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Eq, PartialEq, Serialize)] | #[derive(Debug, Clone, Eq, PartialEq, Serialize)] | ||||||
| pub struct BankDetails { | pub struct BankDetails { | ||||||
|     #[serde(rename = "bankAccount.holder")] |     #[serde(rename = "bankAccount.holder")] | ||||||
| @ -128,7 +330,7 @@ pub struct BankDetails { | |||||||
| } | } | ||||||
|  |  | ||||||
| #[allow(dead_code)] | #[allow(dead_code)] | ||||||
| #[derive(Debug, Default, Clone, Eq, PartialEq, Serialize)] | #[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] | ||||||
| pub enum AciPaymentType { | pub enum AciPaymentType { | ||||||
|     #[serde(rename = "PA")] |     #[serde(rename = "PA")] | ||||||
|     Preauthorization, |     Preauthorization, | ||||||
| @ -148,177 +350,187 @@ pub enum AciPaymentType { | |||||||
| impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest { | impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest { | ||||||
|     type Error = error_stack::Report<errors::ConnectorError>; |     type Error = error_stack::Report<errors::ConnectorError>; | ||||||
|     fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> { |     fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result<Self, Self::Error> { | ||||||
|         let payment_details: PaymentDetails = match item.request.payment_method_data.clone() { |         match item.request.payment_method_data.clone() { | ||||||
|             api::PaymentMethodData::Card(ccard) => PaymentDetails::AciCard(Box::new(CardDetails { |             api::PaymentMethodData::Card(ref card_data) => Self::try_from((item, card_data)), | ||||||
|                 card_number: ccard.card_number, |             api::PaymentMethodData::Wallet(ref wallet_data) => Self::try_from((item, wallet_data)), | ||||||
|                 card_holder: ccard.card_holder_name, |             api::PaymentMethodData::PayLater(ref pay_later_data) => { | ||||||
|                 card_expiry_month: ccard.card_exp_month, |                 Self::try_from((item, pay_later_data)) | ||||||
|                 card_expiry_year: ccard.card_exp_year, |  | ||||||
|                 card_cvv: ccard.card_cvc, |  | ||||||
|             })), |  | ||||||
|             api::PaymentMethodData::PayLater(_) => PaymentDetails::Klarna, |  | ||||||
|             api::PaymentMethodData::Wallet(ref wallet_data) => match wallet_data { |  | ||||||
|                 api_models::payments::WalletData::MbWayRedirect(data) => { |  | ||||||
|                     PaymentDetails::Wallet(Box::new(WalletPMData { |  | ||||||
|                         payment_brand: PaymentBrand::Mbway, |  | ||||||
|                         account_id: Some(data.telephone_number.clone()), |  | ||||||
|                         shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                     })) |  | ||||||
|             } |             } | ||||||
|                 api_models::payments::WalletData::AliPayRedirect { .. } => { |             api::PaymentMethodData::BankRedirect(ref bank_redirect_data) => { | ||||||
|                     PaymentDetails::Wallet(Box::new(WalletPMData { |                 Self::try_from((item, bank_redirect_data)) | ||||||
|                         payment_brand: PaymentBrand::AliPay, |  | ||||||
|                         account_id: None, |  | ||||||
|                         shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                     })) |  | ||||||
|             } |             } | ||||||
|                 _ => Err(errors::ConnectorError::NotImplemented( |             api::PaymentMethodData::MandatePayment => { | ||||||
|                     "Payment method".to_string(), |                 let mandate_id = item.request.mandate_id.clone().ok_or( | ||||||
|                 ))?, |  | ||||||
|             }, |  | ||||||
|             api::PaymentMethodData::BankRedirect(ref redirect_banking_data) => { |  | ||||||
|                 match redirect_banking_data { |  | ||||||
|                     api_models::payments::BankRedirectData::Eps { .. } => { |  | ||||||
|                         PaymentDetails::BankRedirect(Box::new(BankRedirectionPMData { |  | ||||||
|                             payment_brand: PaymentBrand::Eps, |  | ||||||
|                             bank_account_country: Some(api_models::enums::CountryAlpha2::AT), |  | ||||||
|                             bank_account_bank_name: None, |  | ||||||
|                             bank_account_bic: None, |  | ||||||
|                             bank_account_iban: None, |  | ||||||
|                             billing_country: None, |  | ||||||
|                             merchant_customer_id: None, |  | ||||||
|                             merchant_transaction_id: None, |  | ||||||
|                             customer_email: None, |  | ||||||
|                             shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                         })) |  | ||||||
|                     } |  | ||||||
|                     api_models::payments::BankRedirectData::Giropay { |  | ||||||
|                         bank_account_bic, |  | ||||||
|                         bank_account_iban, |  | ||||||
|                         .. |  | ||||||
|                     } => PaymentDetails::BankRedirect(Box::new(BankRedirectionPMData { |  | ||||||
|                         payment_brand: PaymentBrand::Giropay, |  | ||||||
|                         bank_account_country: Some(api_models::enums::CountryAlpha2::DE), |  | ||||||
|                         bank_account_bank_name: None, |  | ||||||
|                         bank_account_bic: bank_account_bic.clone(), |  | ||||||
|                         bank_account_iban: bank_account_iban.clone(), |  | ||||||
|                         billing_country: None, |  | ||||||
|                         merchant_customer_id: None, |  | ||||||
|                         merchant_transaction_id: None, |  | ||||||
|                         customer_email: None, |  | ||||||
|                         shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                     })), |  | ||||||
|                     api_models::payments::BankRedirectData::Ideal { bank_name, .. } => { |  | ||||||
|                         PaymentDetails::BankRedirect(Box::new(BankRedirectionPMData { |  | ||||||
|                             payment_brand: PaymentBrand::Ideal, |  | ||||||
|                             bank_account_country: Some(api_models::enums::CountryAlpha2::NL), |  | ||||||
|                             bank_account_bank_name: bank_name |  | ||||||
|                                 .map(|bank_name| bank_name.to_string()), |  | ||||||
|                             bank_account_bic: None, |  | ||||||
|                             bank_account_iban: None, |  | ||||||
|                             billing_country: None, |  | ||||||
|                             merchant_customer_id: None, |  | ||||||
|                             merchant_transaction_id: None, |  | ||||||
|                             customer_email: None, |  | ||||||
|                             shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                         })) |  | ||||||
|                     } |  | ||||||
|                     api_models::payments::BankRedirectData::Sofort { country, .. } => { |  | ||||||
|                         PaymentDetails::BankRedirect(Box::new(BankRedirectionPMData { |  | ||||||
|                             payment_brand: PaymentBrand::Sofortueberweisung, |  | ||||||
|                             bank_account_country: Some(*country), |  | ||||||
|                             bank_account_bank_name: None, |  | ||||||
|                             bank_account_bic: None, |  | ||||||
|                             bank_account_iban: None, |  | ||||||
|                             billing_country: None, |  | ||||||
|                             merchant_customer_id: None, |  | ||||||
|                             merchant_transaction_id: None, |  | ||||||
|                             customer_email: None, |  | ||||||
|                             shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                         })) |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     api_models::payments::BankRedirectData::Przelewy24 { |  | ||||||
|                         billing_details, .. |  | ||||||
|                     } => PaymentDetails::BankRedirect(Box::new(BankRedirectionPMData { |  | ||||||
|                         payment_brand: PaymentBrand::Przelewy, |  | ||||||
|                         bank_account_country: None, |  | ||||||
|                         bank_account_bank_name: None, |  | ||||||
|                         bank_account_bic: None, |  | ||||||
|                         bank_account_iban: None, |  | ||||||
|                         billing_country: None, |  | ||||||
|                         merchant_customer_id: None, |  | ||||||
|                         merchant_transaction_id: None, |  | ||||||
|                         customer_email: billing_details.email.clone(), |  | ||||||
|                         shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                     })), |  | ||||||
|  |  | ||||||
|                     api_models::payments::BankRedirectData::Interac { email, country } => { |  | ||||||
|                         PaymentDetails::BankRedirect(Box::new(BankRedirectionPMData { |  | ||||||
|                             payment_brand: PaymentBrand::InteracOnline, |  | ||||||
|                             bank_account_country: Some(*country), |  | ||||||
|                             bank_account_bank_name: None, |  | ||||||
|                             bank_account_bic: None, |  | ||||||
|                             bank_account_iban: None, |  | ||||||
|                             billing_country: None, |  | ||||||
|                             merchant_customer_id: None, |  | ||||||
|                             merchant_transaction_id: None, |  | ||||||
|                             customer_email: Some(email.to_owned()), |  | ||||||
|                             shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                         })) |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     api_models::payments::BankRedirectData::Trustly { country } => { |  | ||||||
|                         PaymentDetails::BankRedirect(Box::new(BankRedirectionPMData { |  | ||||||
|                             payment_brand: PaymentBrand::Trustly, |  | ||||||
|                             bank_account_country: None, |  | ||||||
|                             bank_account_bank_name: None, |  | ||||||
|                             bank_account_bic: None, |  | ||||||
|                             bank_account_iban: None, |  | ||||||
|                             billing_country: Some(*country), |  | ||||||
|                             merchant_customer_id: Some(Secret::new( |  | ||||||
|                                 item.customer_id.clone().ok_or( |  | ||||||
|                     errors::ConnectorError::MissingRequiredField { |                     errors::ConnectorError::MissingRequiredField { | ||||||
|                                         field_name: "customer_id", |                         field_name: "mandate_id", | ||||||
|                     }, |                     }, | ||||||
|                                 )?, |                 )?; | ||||||
|                             )), |                 Self::try_from((item, mandate_id)) | ||||||
|                             merchant_transaction_id: Some(Secret::new(item.payment_id.clone())), |  | ||||||
|                             customer_email: None, |  | ||||||
|                             shopper_result_url: item.request.router_return_url.clone(), |  | ||||||
|                         })) |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|                     _ => Err(errors::ConnectorError::NotImplemented( |  | ||||||
|                         "Payment method".to_string(), |  | ||||||
|                     ))?, |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             api::PaymentMethodData::Crypto(_) |             api::PaymentMethodData::Crypto(_) | ||||||
|             | api::PaymentMethodData::BankDebit(_) |             | api::PaymentMethodData::BankDebit(_) | ||||||
|             | api::PaymentMethodData::BankTransfer(_) |             | api::PaymentMethodData::BankTransfer(_) | ||||||
|             | api::PaymentMethodData::Reward(_) |             | api::PaymentMethodData::Reward(_) => Err(errors::ConnectorError::NotSupported { | ||||||
|             | api::PaymentMethodData::MandatePayment => { |  | ||||||
|                 Err(errors::ConnectorError::NotSupported { |  | ||||||
|                 message: format!("{:?}", item.payment_method), |                 message: format!("{:?}", item.payment_method), | ||||||
|                 connector: "Aci", |                 connector: "Aci", | ||||||
|                     payment_experience: api_models::enums::PaymentExperience::RedirectToUrl |                 payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(), | ||||||
|                         .to_string(), |             })?, | ||||||
|                 })? |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|         }; |  | ||||||
|  |  | ||||||
|  | impl | ||||||
|  |     TryFrom<( | ||||||
|  |         &types::PaymentsAuthorizeRouterData, | ||||||
|  |         &api_models::payments::WalletData, | ||||||
|  |     )> for AciPaymentsRequest | ||||||
|  | { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from( | ||||||
|  |         value: ( | ||||||
|  |             &types::PaymentsAuthorizeRouterData, | ||||||
|  |             &api_models::payments::WalletData, | ||||||
|  |         ), | ||||||
|  |     ) -> Result<Self, Self::Error> { | ||||||
|  |         let (item, wallet_data) = value; | ||||||
|  |         let txn_details = get_transaction_details(item)?; | ||||||
|  |         let payment_method = PaymentDetails::try_from(wallet_data)?; | ||||||
|  |  | ||||||
|  |         Ok(Self { | ||||||
|  |             txn_details, | ||||||
|  |             payment_method, | ||||||
|  |             instruction: None, | ||||||
|  |             shopper_result_url: item.request.router_return_url.clone(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl | ||||||
|  |     TryFrom<( | ||||||
|  |         &types::PaymentsAuthorizeRouterData, | ||||||
|  |         &api_models::payments::BankRedirectData, | ||||||
|  |     )> for AciPaymentsRequest | ||||||
|  | { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from( | ||||||
|  |         value: ( | ||||||
|  |             &types::PaymentsAuthorizeRouterData, | ||||||
|  |             &api_models::payments::BankRedirectData, | ||||||
|  |         ), | ||||||
|  |     ) -> Result<Self, Self::Error> { | ||||||
|  |         let (item, bank_redirect_data) = value; | ||||||
|  |         let txn_details = get_transaction_details(item)?; | ||||||
|  |         let payment_method = PaymentDetails::try_from((item, bank_redirect_data))?; | ||||||
|  |  | ||||||
|  |         Ok(Self { | ||||||
|  |             txn_details, | ||||||
|  |             payment_method, | ||||||
|  |             instruction: None, | ||||||
|  |             shopper_result_url: item.request.router_return_url.clone(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl | ||||||
|  |     TryFrom<( | ||||||
|  |         &types::PaymentsAuthorizeRouterData, | ||||||
|  |         &api_models::payments::PayLaterData, | ||||||
|  |     )> for AciPaymentsRequest | ||||||
|  | { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from( | ||||||
|  |         value: ( | ||||||
|  |             &types::PaymentsAuthorizeRouterData, | ||||||
|  |             &api_models::payments::PayLaterData, | ||||||
|  |         ), | ||||||
|  |     ) -> Result<Self, Self::Error> { | ||||||
|  |         let (item, _pay_later_data) = value; | ||||||
|  |         let txn_details = get_transaction_details(item)?; | ||||||
|  |         let payment_method = PaymentDetails::Klarna; | ||||||
|  |  | ||||||
|  |         Ok(Self { | ||||||
|  |             txn_details, | ||||||
|  |             payment_method, | ||||||
|  |             instruction: None, | ||||||
|  |             shopper_result_url: item.request.router_return_url.clone(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl TryFrom<(&types::PaymentsAuthorizeRouterData, &api::Card)> for AciPaymentsRequest { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from( | ||||||
|  |         value: (&types::PaymentsAuthorizeRouterData, &api::Card), | ||||||
|  |     ) -> Result<Self, Self::Error> { | ||||||
|  |         let (item, card_data) = value; | ||||||
|  |         let txn_details = get_transaction_details(item)?; | ||||||
|  |         let payment_method = PaymentDetails::try_from(card_data.clone())?; | ||||||
|  |         let instruction = get_instruction_details(item); | ||||||
|  |  | ||||||
|  |         Ok(Self { | ||||||
|  |             txn_details, | ||||||
|  |             payment_method, | ||||||
|  |             instruction, | ||||||
|  |             shopper_result_url: None, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl | ||||||
|  |     TryFrom<( | ||||||
|  |         &types::PaymentsAuthorizeRouterData, | ||||||
|  |         api_models::payments::MandateIds, | ||||||
|  |     )> for AciPaymentsRequest | ||||||
|  | { | ||||||
|  |     type Error = Error; | ||||||
|  |     fn try_from( | ||||||
|  |         value: ( | ||||||
|  |             &types::PaymentsAuthorizeRouterData, | ||||||
|  |             api_models::payments::MandateIds, | ||||||
|  |         ), | ||||||
|  |     ) -> Result<Self, Self::Error> { | ||||||
|  |         let (item, _mandate_data) = value; | ||||||
|  |         let instruction = get_instruction_details(item); | ||||||
|  |         let txn_details = get_transaction_details(item)?; | ||||||
|  |  | ||||||
|  |         Ok(Self { | ||||||
|  |             txn_details, | ||||||
|  |             payment_method: PaymentDetails::Mandate, | ||||||
|  |             instruction, | ||||||
|  |             shopper_result_url: item.request.router_return_url.clone(), | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | fn get_transaction_details( | ||||||
|  |     item: &types::PaymentsAuthorizeRouterData, | ||||||
|  | ) -> Result<TransactionDetails, error_stack::Report<errors::ConnectorError>> { | ||||||
|     let auth = AciAuthType::try_from(&item.connector_auth_type)?; |     let auth = AciAuthType::try_from(&item.connector_auth_type)?; | ||||||
|         let aci_payment_request = Self { |     Ok(TransactionDetails { | ||||||
|             payment_method: payment_details, |  | ||||||
|         entity_id: auth.entity_id, |         entity_id: auth.entity_id, | ||||||
|         amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?, |         amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?, | ||||||
|         currency: item.request.currency.to_string(), |         currency: item.request.currency.to_string(), | ||||||
|         payment_type: AciPaymentType::Debit, |         payment_type: AciPaymentType::Debit, | ||||||
|         }; |     }) | ||||||
|         Ok(aci_payment_request) |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | fn get_instruction_details(item: &types::PaymentsAuthorizeRouterData) -> Option<Instruction> { | ||||||
|  |     if item.request.setup_mandate_details.is_some() { | ||||||
|  |         return Some(Instruction { | ||||||
|  |             mode: InstructionMode::Initial, | ||||||
|  |             transaction_type: InstructionType::Unscheduled, | ||||||
|  |             source: InstructionSource::Cit, | ||||||
|  |             create_registration: Some(true), | ||||||
|  |         }); | ||||||
|  |     } else if item.request.mandate_id.is_some() { | ||||||
|  |         return Some(Instruction { | ||||||
|  |             mode: InstructionMode::Repeated, | ||||||
|  |             transaction_type: InstructionType::Unscheduled, | ||||||
|  |             source: InstructionSource::Mit, | ||||||
|  |             create_registration: None, | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  |     None | ||||||
| } | } | ||||||
|  |  | ||||||
| impl TryFrom<&types::PaymentsCancelRouterData> for AciCancelRequest { | impl TryFrom<&types::PaymentsCancelRouterData> for AciCancelRequest { | ||||||
| @ -374,6 +586,7 @@ impl FromStr for AciPaymentStatus { | |||||||
| #[serde(rename_all = "camelCase")] | #[serde(rename_all = "camelCase")] | ||||||
| pub struct AciPaymentsResponse { | pub struct AciPaymentsResponse { | ||||||
|     id: String, |     id: String, | ||||||
|  |     registration_id: Option<String>, | ||||||
|     // ndc is an internal unique identifier for the request. |     // ndc is an internal unique identifier for the request. | ||||||
|     ndc: String, |     ndc: String, | ||||||
|     timestamp: String, |     timestamp: String, | ||||||
| @ -437,6 +650,14 @@ impl<F, T> | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         let mandate_reference = item | ||||||
|  |             .response | ||||||
|  |             .registration_id | ||||||
|  |             .map(|id| types::MandateReference { | ||||||
|  |                 connector_mandate_id: Some(id), | ||||||
|  |                 payment_method_id: None, | ||||||
|  |             }); | ||||||
|  |  | ||||||
|         Ok(Self { |         Ok(Self { | ||||||
|             status: { |             status: { | ||||||
|                 if redirection_data.is_some() { |                 if redirection_data.is_some() { | ||||||
| @ -450,7 +671,7 @@ impl<F, T> | |||||||
|             response: Ok(types::PaymentsResponseData::TransactionResponse { |             response: Ok(types::PaymentsResponseData::TransactionResponse { | ||||||
|                 resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), |                 resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), | ||||||
|                 redirection_data, |                 redirection_data, | ||||||
|                 mandate_reference: None, |                 mandate_reference, | ||||||
|                 connector_metadata: None, |                 connector_metadata: None, | ||||||
|                 network_txn_id: None, |                 network_txn_id: None, | ||||||
|             }), |             }), | ||||||
|  | |||||||
| @ -267,6 +267,8 @@ pub enum ConnectorError { | |||||||
|     FlowNotSupported { flow: String, connector: String }, |     FlowNotSupported { flow: String, connector: String }, | ||||||
|     #[error("Capture method not supported")] |     #[error("Capture method not supported")] | ||||||
|     CaptureMethodNotSupported, |     CaptureMethodNotSupported, | ||||||
|  |     #[error("Missing connector mandate ID")] | ||||||
|  |     MissingConnectorMandateID, | ||||||
|     #[error("Missing connector transaction ID")] |     #[error("Missing connector transaction ID")] | ||||||
|     MissingConnectorTransactionID, |     MissingConnectorTransactionID, | ||||||
|     #[error("Missing connector refund ID")] |     #[error("Missing connector refund ID")] | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Sakil Mostak
					Sakil Mostak