feat(connector): [ACI] implement Card Mandates for ACI (#1174)

This commit is contained in:
Sakil Mostak
2023-06-30 13:11:17 +05:30
committed by GitHub
parent cd4dbcb3f6
commit 15c2a70b42
3 changed files with 407 additions and 176 deletions

View File

@ -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(

View File

@ -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,
}), }),

View File

@ -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")]