mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-30 09:38:33 +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,179 +350,189 @@ 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::BankRedirect(ref bank_redirect_data) => {
|
||||||
})),
|
Self::try_from((item, bank_redirect_data))
|
||||||
api::PaymentMethodData::PayLater(_) => PaymentDetails::Klarna,
|
}
|
||||||
api::PaymentMethodData::Wallet(ref wallet_data) => match wallet_data {
|
api::PaymentMethodData::MandatePayment => {
|
||||||
api_models::payments::WalletData::MbWayRedirect(data) => {
|
let mandate_id = item.request.mandate_id.clone().ok_or(
|
||||||
PaymentDetails::Wallet(Box::new(WalletPMData {
|
errors::ConnectorError::MissingRequiredField {
|
||||||
payment_brand: PaymentBrand::Mbway,
|
field_name: "mandate_id",
|
||||||
account_id: Some(data.telephone_number.clone()),
|
},
|
||||||
shopper_result_url: item.request.router_return_url.clone(),
|
)?;
|
||||||
}))
|
Self::try_from((item, mandate_id))
|
||||||
}
|
|
||||||
api_models::payments::WalletData::AliPayRedirect { .. } => {
|
|
||||||
PaymentDetails::Wallet(Box::new(WalletPMData {
|
|
||||||
payment_brand: PaymentBrand::AliPay,
|
|
||||||
account_id: None,
|
|
||||||
shopper_result_url: item.request.router_return_url.clone(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
_ => Err(errors::ConnectorError::NotImplemented(
|
|
||||||
"Payment method".to_string(),
|
|
||||||
))?,
|
|
||||||
},
|
|
||||||
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 {
|
|
||||||
field_name: "customer_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 => {
|
message: format!("{:?}", item.payment_method),
|
||||||
Err(errors::ConnectorError::NotSupported {
|
connector: "Aci",
|
||||||
message: format!("{:?}", item.payment_method),
|
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl.to_string(),
|
||||||
connector: "Aci",
|
})?,
|
||||||
payment_experience: api_models::enums::PaymentExperience::RedirectToUrl
|
}
|
||||||
.to_string(),
|
|
||||||
})?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let auth = AciAuthType::try_from(&item.connector_auth_type)?;
|
|
||||||
let aci_payment_request = Self {
|
|
||||||
payment_method: payment_details,
|
|
||||||
entity_id: auth.entity_id,
|
|
||||||
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
|
|
||||||
currency: item.request.currency.to_string(),
|
|
||||||
payment_type: AciPaymentType::Debit,
|
|
||||||
};
|
|
||||||
Ok(aci_payment_request)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)?;
|
||||||
|
Ok(TransactionDetails {
|
||||||
|
entity_id: auth.entity_id,
|
||||||
|
amount: utils::to_currency_base_unit(item.request.amount, item.request.currency)?,
|
||||||
|
currency: item.request.currency.to_string(),
|
||||||
|
payment_type: AciPaymentType::Debit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
type Error = error_stack::Report<errors::ConnectorError>;
|
type Error = error_stack::Report<errors::ConnectorError>;
|
||||||
fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
fn try_from(item: &types::PaymentsCancelRouterData) -> Result<Self, Self::Error> {
|
||||||
@ -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