use std::str::FromStr; use error_stack::report; use masking::Secret; use serde::{Deserialize, Serialize}; use super::result_codes::{FAILURE_CODES, PENDING_CODES, SUCCESSFUL_CODES}; use crate::{ core::errors, types::{self, api, storage::enums}, }; pub struct AciAuthType { pub api_key: String, pub entity_id: String, } impl TryFrom<&types::ConnectorAuthType> for AciAuthType { type Error = error_stack::Report; fn try_from(item: &types::ConnectorAuthType) -> Result { if let types::ConnectorAuthType::BodyKey { api_key, key1 } = item { Ok(Self { api_key: api_key.to_string(), entity_id: key1.to_string(), }) } else { Err(errors::ConnectorError::FailedToObtainAuthType)? } } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct AciPaymentsRequest { pub entity_id: String, pub amount: i64, pub currency: String, pub payment_type: AciPaymentType, #[serde(flatten)] pub payment_method: PaymentDetails, } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct AciCancelRequest { pub entity_id: String, pub payment_type: AciPaymentType, } #[derive(Debug, Clone, Eq, PartialEq, Serialize)] #[serde(untagged)] pub enum PaymentDetails { #[serde(rename = "card")] Card(CardDetails), #[serde(rename = "bank")] BankAccount(BankDetails), Wallet, Klarna, Paypal, } #[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct CardDetails { #[serde(rename = "card.number")] pub card_number: Secret, #[serde(rename = "card.holder")] pub card_holder: Secret, #[serde(rename = "card.expiryMonth")] pub card_expiry_month: Secret, #[serde(rename = "card.expiryYear")] pub card_expiry_year: Secret, #[serde(rename = "card.cvv")] pub card_cvv: Secret, } #[derive(Debug, Clone, Eq, PartialEq, Serialize)] pub struct BankDetails { #[serde(rename = "bankAccount.holder")] pub account_holder: String, } #[allow(dead_code)] #[derive(Debug, Default, Clone, Eq, PartialEq, Serialize)] pub enum AciPaymentType { #[serde(rename = "PA")] Preauthorization, #[default] #[serde(rename = "DB")] Debit, #[serde(rename = "CD")] Credit, #[serde(rename = "CP")] Capture, #[serde(rename = "RV")] Reversal, #[serde(rename = "RF")] Refund, } impl TryFrom<&types::PaymentsAuthorizeRouterData> for AciPaymentsRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsAuthorizeRouterData) -> Result { let payment_details: PaymentDetails = match item.request.payment_method_data.clone() { api::PaymentMethod::Card(ccard) => PaymentDetails::Card(CardDetails { card_number: ccard.card_number, card_holder: ccard.card_holder_name, card_expiry_month: ccard.card_exp_month, card_expiry_year: ccard.card_exp_year, card_cvv: ccard.card_cvc, }), api::PaymentMethod::BankTransfer => PaymentDetails::BankAccount(BankDetails { account_holder: "xyz".to_string(), }), api::PaymentMethod::PayLater(_) => PaymentDetails::Klarna, api::PaymentMethod::Wallet(_) => PaymentDetails::Wallet, api::PaymentMethod::Paypal => PaymentDetails::Paypal, }; let auth = AciAuthType::try_from(&item.connector_auth_type)?; let aci_payment_request = Self { payment_method: payment_details, entity_id: auth.entity_id, amount: item.request.amount, currency: item.request.currency.to_string(), payment_type: AciPaymentType::Debit, }; Ok(aci_payment_request) } } impl TryFrom<&types::PaymentsCancelRouterData> for AciCancelRequest { type Error = error_stack::Report; fn try_from(item: &types::PaymentsCancelRouterData) -> Result { let auth = AciAuthType::try_from(&item.connector_auth_type)?; let aci_payment_request = Self { entity_id: auth.entity_id, payment_type: AciPaymentType::Reversal, }; Ok(aci_payment_request) } } #[derive(Debug, Default, Clone, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum AciPaymentStatus { Succeeded, Failed, #[default] Pending, } impl From for enums::AttemptStatus { fn from(item: AciPaymentStatus) -> Self { match item { AciPaymentStatus::Succeeded => Self::Charged, AciPaymentStatus::Failed => Self::Failure, AciPaymentStatus::Pending => Self::Authorizing, } } } impl FromStr for AciPaymentStatus { type Err = error_stack::Report; fn from_str(s: &str) -> Result { if FAILURE_CODES.contains(&s) { Ok(Self::Failed) } else if PENDING_CODES.contains(&s) { Ok(Self::Pending) } else if SUCCESSFUL_CODES.contains(&s) { Ok(Self::Succeeded) } else { Err(report!(errors::ConnectorError::UnexpectedResponseError( bytes::Bytes::from(s.to_owned()) ))) } } } #[derive(Default, Clone, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct AciPaymentsResponse { id: String, // ndc is an internal unique identifier for the request. ndc: String, timestamp: String, build_number: String, pub(super) result: ResultCode, } #[derive(Default, Debug, Clone, Deserialize, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ResultCode { pub(super) code: String, pub(super) description: String, pub(super) parameter_errors: Option>, } #[derive(Default, Debug, Clone, Deserialize, PartialEq, Eq)] pub struct ErrorParameters { pub(super) name: String, pub(super) value: String, pub(super) message: String, } impl TryFrom> for types::RouterData { type Error = error_stack::Report; fn try_from( item: types::ResponseRouterData, ) -> Result { Ok(Self { status: enums::AttemptStatus::from(AciPaymentStatus::from_str( &item.response.result.code, )?), response: Ok(types::PaymentsResponseData::TransactionResponse { resource_id: types::ResponseId::ConnectorTransactionId(item.response.id), redirection_data: None, mandate_reference: None, connector_metadata: None, }), ..item.data }) } } #[derive(Default, Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct AciRefundRequest { pub amount: i64, pub currency: String, pub payment_type: AciPaymentType, pub entity_id: String, } impl TryFrom<&types::RefundsRouterData> for AciRefundRequest { type Error = error_stack::Report; fn try_from(item: &types::RefundsRouterData) -> Result { let amount = item.request.refund_amount; let currency = item.request.currency; let payment_type = AciPaymentType::Refund; let auth = AciAuthType::try_from(&item.connector_auth_type)?; Ok(Self { amount, currency: currency.to_string(), payment_type, entity_id: auth.entity_id, }) } } #[derive(Debug, Default, Deserialize, Clone)] pub enum AciRefundStatus { Succeeded, Failed, #[default] Pending, } impl FromStr for AciRefundStatus { type Err = error_stack::Report; fn from_str(s: &str) -> Result { if FAILURE_CODES.contains(&s) { Ok(Self::Failed) } else if PENDING_CODES.contains(&s) { Ok(Self::Pending) } else if SUCCESSFUL_CODES.contains(&s) { Ok(Self::Succeeded) } else { Err(report!(errors::ConnectorError::UnexpectedResponseError( bytes::Bytes::from(s.to_owned()) ))) } } } impl From for enums::RefundStatus { fn from(item: AciRefundStatus) -> Self { match item { AciRefundStatus::Succeeded => Self::Success, AciRefundStatus::Failed => Self::Failure, AciRefundStatus::Pending => Self::Pending, } } } #[allow(dead_code)] #[derive(Debug, Clone, Deserialize)] #[serde(rename_all = "camelCase")] pub struct AciRefundResponse { id: String, //ndc is an internal unique identifier for the request. ndc: String, timestamp: String, build_number: String, pub(super) result: ResultCode, } impl TryFrom> for types::RefundsRouterData { type Error = error_stack::Report; fn try_from( item: types::RefundsResponseRouterData, ) -> Result { Ok(Self { response: Ok(types::RefundsResponseData { connector_refund_id: item.response.id, refund_status: enums::RefundStatus::from(AciRefundStatus::from_str( &item.response.result.code, )?), }), ..item.data }) } }