feat(connector): [VOLT] Implement payment flows and bank redirect payment method (#2582)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Prasunna Soppa <70575890+prasunna09@users.noreply.github.com>
This commit is contained in:
Swangi Kumari
2023-10-30 13:13:28 +05:30
committed by GitHub
parent 8125ea1991
commit 23bd364a78
94 changed files with 2972 additions and 148 deletions

View File

@ -3,7 +3,7 @@ pub mod transformers;
use std::fmt::Debug;
use error_stack::{IntoReport, ResultExt};
use masking::ExposeInterface;
use masking::{ExposeInterface, PeekInterface};
use transformers as volt;
use crate::{
@ -64,8 +64,15 @@ where
.to_string()
.into(),
)];
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
header.append(&mut api_key);
let access_token = req
.access_token
.clone()
.ok_or(errors::ConnectorError::FailedToObtainAuthType)?;
let auth_header = (
headers::AUTHORIZATION.to_string(),
format!("Bearer {}", access_token.token.peek()).into_masked(),
);
header.push(auth_header);
Ok(header)
}
}
@ -95,7 +102,7 @@ impl ConnectorCommon for Volt {
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
Ok(vec![(
headers::AUTHORIZATION.to_string(),
auth.api_key.expose().into_masked(),
auth.username.expose().into_masked(),
)])
}
@ -108,11 +115,20 @@ impl ConnectorCommon for Volt {
.parse_struct("VoltErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
let reason = match &response.exception.error_list {
Some(error_list) => error_list
.iter()
.map(|error| error.message.clone())
.collect::<Vec<String>>()
.join(" & "),
None => response.exception.message.clone(),
};
Ok(ErrorResponse {
status_code: res.status_code,
code: response.code,
message: response.message,
reason: response.reason,
code: response.exception.message.to_string(),
message: response.exception.message.clone(),
reason: Some(reason),
})
}
}
@ -130,6 +146,84 @@ impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::Payme
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
for Volt
{
fn get_url(
&self,
_req: &types::RefreshTokenRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!("{}oauth", self.base_url(connectors)))
}
fn get_content_type(&self) -> &'static str {
"application/x-www-form-urlencoded"
}
fn get_headers(
&self,
_req: &types::RefreshTokenRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
Ok(vec![(
headers::CONTENT_TYPE.to_string(),
types::RefreshTokenType::get_content_type(self)
.to_string()
.into(),
)])
}
fn get_request_body(
&self,
req: &types::RefreshTokenRouterData,
) -> CustomResult<Option<types::RequestBody>, errors::ConnectorError> {
let req_obj = volt::VoltAuthUpdateRequest::try_from(req)?;
let volt_req = types::RequestBody::log_and_get_request_body(
&req_obj,
utils::Encode::<volt::VoltAuthUpdateRequest>::url_encode,
)
.change_context(errors::ConnectorError::RequestEncodingFailed)?;
Ok(Some(volt_req))
}
fn build_request(
&self,
req: &types::RefreshTokenRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
let req = Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.attach_default_headers()
.headers(types::RefreshTokenType::get_headers(self, req, connectors)?)
.url(&types::RefreshTokenType::get_url(self, req, connectors)?)
.body(types::RefreshTokenType::get_request_body(self, req)?)
.build(),
);
Ok(req)
}
fn handle_response(
&self,
data: &types::RefreshTokenRouterData,
res: Response,
) -> CustomResult<types::RefreshTokenRouterData, errors::ConnectorError> {
let response: volt::VoltAuthUpdateResponse = res
.response
.parse_struct("Volt VoltAuthUpdateResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res)
}
}
impl
@ -159,9 +253,9 @@ impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::P
fn get_url(
&self,
_req: &types::PaymentsAuthorizeRouterData,
_connectors: &settings::Connectors,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
Ok(format!("{}v2/payments", self.base_url(connectors)))
}
fn get_request_body(
@ -244,10 +338,18 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
fn get_url(
&self,
_req: &types::PaymentsSyncRouterData,
_connectors: &settings::Connectors,
req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
let connector_payment_id = req
.request
.connector_transaction_id
.get_connector_transaction_id()
.change_context(errors::ConnectorError::MissingConnectorTransactionID)?;
Ok(format!(
"{}payments/{connector_payment_id}",
self.base_url(connectors)
))
}
fn build_request(
@ -270,7 +372,7 @@ impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsRe
data: &types::PaymentsSyncRouterData,
res: Response,
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
let response: volt::VoltPaymentsResponse = res
let response: volt::VoltPsyncResponse = res
.response
.parse_struct("volt PaymentsSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
@ -381,10 +483,14 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
fn get_url(
&self,
_req: &types::RefundsRouterData<api::Execute>,
_connectors: &settings::Connectors,
req: &types::RefundsRouterData<api::Execute>,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
let connector_payment_id = req.request.connector_transaction_id.clone();
Ok(format!(
"{}payments/{connector_payment_id}/request-refund",
self.base_url(connectors),
))
}
fn get_request_body(
@ -448,64 +554,7 @@ impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsRespon
}
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Volt {
fn get_headers(
&self,
req: &types::RefundSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_url(
&self,
_req: &types::RefundSyncRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
}
fn build_request(
&self,
req: &types::RefundSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Get)
.url(&types::RefundSyncType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::RefundSyncType::get_headers(self, req, connectors)?)
.body(types::RefundSyncType::get_request_body(self, req)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::RefundSyncRouterData,
res: Response,
) -> CustomResult<types::RefundSyncRouterData, errors::ConnectorError> {
let response: volt::RefundResponse =
res.response
.parse_struct("volt RefundSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res)
}
//Volt does not support Refund Sync
}
#[async_trait::async_trait]

View File

@ -1,12 +1,17 @@
use common_utils::pii::Email;
use diesel_models::enums;
use masking::Secret;
use serde::{Deserialize, Serialize};
use crate::{
connector::utils::PaymentsAuthorizeRequestData,
connector::utils::{self, AddressDetailsData, RouterData},
core::errors,
types::{self, api, storage::enums},
services,
types::{self, api, storage::enums as storage_enums},
};
const PASSWORD: &str = "password";
pub struct VoltRouterData<T> {
pub amount: i64, // The type of amount that a connector accepts, for example, String, i64, f64, etc.
pub router_data: T,
@ -29,7 +34,6 @@ impl<T>
T,
),
) -> Result<Self, Self::Error> {
//Todo : use utils to convert the amount to the type of amount that a connector accepts
Ok(Self {
amount,
router_data: item,
@ -37,20 +41,38 @@ impl<T>
}
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VoltPaymentsRequest {
amount: i64,
card: VoltCard,
currency_code: storage_enums::Currency,
#[serde(rename = "type")]
transaction_type: TransactionType,
merchant_internal_reference: String,
shopper: ShopperDetails,
notification_url: Option<String>,
payment_success_url: Option<String>,
payment_failure_url: Option<String>,
payment_pending_url: Option<String>,
payment_cancel_url: Option<String>,
}
#[derive(Default, Debug, Serialize, Eq, PartialEq)]
pub struct VoltCard {
name: Secret<String>,
number: cards::CardNumber,
expiry_month: Secret<String>,
expiry_year: Secret<String>,
cvc: Secret<String>,
complete: bool,
#[derive(Debug, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum TransactionType {
Bills,
Goods,
PersonToPerson,
Other,
Services,
}
#[derive(Debug, Serialize)]
pub struct ShopperDetails {
reference: String,
email: Option<Email>,
first_name: Secret<String>,
last_name: Secret<String>,
}
impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPaymentsRequest {
@ -59,63 +81,184 @@ impl TryFrom<&VoltRouterData<&types::PaymentsAuthorizeRouterData>> for VoltPayme
item: &VoltRouterData<&types::PaymentsAuthorizeRouterData>,
) -> Result<Self, Self::Error> {
match item.router_data.request.payment_method_data.clone() {
api::PaymentMethodData::Card(req_card) => {
let card = VoltCard {
name: req_card.card_holder_name,
number: req_card.card_number,
expiry_month: req_card.card_exp_month,
expiry_year: req_card.card_exp_year,
cvc: req_card.card_cvc,
complete: item.router_data.request.is_auto_capture()?,
};
Ok(Self {
amount: item.amount.to_owned(),
card,
})
api::PaymentMethodData::BankRedirect(ref bank_redirect) => match bank_redirect {
api_models::payments::BankRedirectData::OpenBankingUk { .. } => {
let amount = item.amount;
let currency_code = item.router_data.request.currency;
let merchant_internal_reference =
item.router_data.connector_request_reference_id.clone();
let payment_success_url = item.router_data.request.router_return_url.clone();
let payment_failure_url = item.router_data.request.router_return_url.clone();
let payment_pending_url = item.router_data.request.router_return_url.clone();
let payment_cancel_url = item.router_data.request.router_return_url.clone();
let notification_url = item.router_data.request.webhook_url.clone();
let address = item.router_data.get_billing_address()?;
let shopper = ShopperDetails {
email: item.router_data.request.email.clone(),
first_name: address.get_first_name()?.to_owned(),
last_name: address.get_last_name()?.to_owned(),
reference: item.router_data.get_customer_id()?.to_owned(),
};
let transaction_type = TransactionType::Services; //transaction_type is a form of enum, it is pre defined and value for this can not be taken from user so we are keeping it as Services as this transaction is type of service.
Ok(Self {
amount,
currency_code,
merchant_internal_reference,
payment_success_url,
payment_failure_url,
payment_pending_url,
payment_cancel_url,
notification_url,
shopper,
transaction_type,
})
}
api_models::payments::BankRedirectData::BancontactCard { .. }
| api_models::payments::BankRedirectData::Bizum {}
| api_models::payments::BankRedirectData::Blik { .. }
| api_models::payments::BankRedirectData::Eps { .. }
| api_models::payments::BankRedirectData::Giropay { .. }
| api_models::payments::BankRedirectData::Ideal { .. }
| api_models::payments::BankRedirectData::Interac { .. }
| api_models::payments::BankRedirectData::OnlineBankingCzechRepublic { .. }
| api_models::payments::BankRedirectData::OnlineBankingFinland { .. }
| api_models::payments::BankRedirectData::OnlineBankingPoland { .. }
| api_models::payments::BankRedirectData::OnlineBankingSlovakia { .. }
| api_models::payments::BankRedirectData::Przelewy24 { .. }
| api_models::payments::BankRedirectData::Sofort { .. }
| api_models::payments::BankRedirectData::Trustly { .. }
| api_models::payments::BankRedirectData::OnlineBankingFpx { .. }
| api_models::payments::BankRedirectData::OnlineBankingThailand { .. } => {
Err(errors::ConnectorError::NotSupported {
message: utils::SELECTED_PAYMENT_METHOD.to_string(),
connector: "Volt",
}
.into())
}
},
api_models::payments::PaymentMethodData::Card(_)
| api_models::payments::PaymentMethodData::CardRedirect(_)
| api_models::payments::PaymentMethodData::Wallet(_)
| api_models::payments::PaymentMethodData::PayLater(_)
| api_models::payments::PaymentMethodData::BankDebit(_)
| api_models::payments::PaymentMethodData::BankTransfer(_)
| api_models::payments::PaymentMethodData::Crypto(_)
| api_models::payments::PaymentMethodData::MandatePayment
| api_models::payments::PaymentMethodData::Reward
| api_models::payments::PaymentMethodData::Upi(_)
| api_models::payments::PaymentMethodData::Voucher(_)
| api_models::payments::PaymentMethodData::GiftCard(_) => {
Err(errors::ConnectorError::NotSupported {
message: utils::SELECTED_PAYMENT_METHOD.to_string(),
connector: "Volt",
}
.into())
}
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
}
}
}
#[derive(Debug, Clone, Serialize, PartialEq)]
pub struct VoltAuthUpdateRequest {
grant_type: String,
client_id: Secret<String>,
client_secret: Secret<String>,
username: Secret<String>,
password: Secret<String>,
}
impl TryFrom<&types::RefreshTokenRouterData> for VoltAuthUpdateRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::RefreshTokenRouterData) -> Result<Self, Self::Error> {
let auth = VoltAuthType::try_from(&item.connector_auth_type)?;
Ok(Self {
grant_type: PASSWORD.to_string(),
username: auth.username,
password: auth.password,
client_id: auth.client_id,
client_secret: auth.client_secret,
})
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct VoltAuthUpdateResponse {
pub access_token: Secret<String>,
pub token_type: String,
pub expires_in: i64,
pub refresh_token: String,
}
impl<F, T> TryFrom<types::ResponseRouterData<F, VoltAuthUpdateResponse, T, types::AccessToken>>
for types::RouterData<F, T, types::AccessToken>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, VoltAuthUpdateResponse, T, types::AccessToken>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::AccessToken {
token: item.response.access_token,
expires: item.response.expires_in,
}),
..item.data
})
}
}
pub struct VoltAuthType {
pub(super) api_key: Secret<String>,
pub(super) username: Secret<String>,
pub(super) password: Secret<String>,
pub(super) client_id: Secret<String>,
pub(super) client_secret: Secret<String>,
}
impl TryFrom<&types::ConnectorAuthType> for VoltAuthType {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
match auth_type {
types::ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
api_key: api_key.to_owned(),
types::ConnectorAuthType::MultiAuthKey {
api_key,
key1,
api_secret,
key2,
} => Ok(Self {
username: api_key.to_owned(),
password: api_secret.to_owned(),
client_id: key1.to_owned(),
client_secret: key2.to_owned(),
}),
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum VoltPaymentStatus {
Succeeded,
Failed,
#[default]
Processing,
}
impl From<VoltPaymentStatus> for enums::AttemptStatus {
fn from(item: VoltPaymentStatus) -> Self {
match item {
VoltPaymentStatus::Succeeded => Self::Charged,
VoltPaymentStatus::Failed => Self::Failure,
VoltPaymentStatus::Processing => Self::Authorizing,
VoltPaymentStatus::Completed
| VoltPaymentStatus::Received
| VoltPaymentStatus::Settled => Self::Charged,
VoltPaymentStatus::DelayedAtBank => Self::Pending,
VoltPaymentStatus::NewPayment
| VoltPaymentStatus::BankRedirect
| VoltPaymentStatus::AwaitingCheckoutAuthorisation => Self::AuthenticationPending,
VoltPaymentStatus::RefusedByBank
| VoltPaymentStatus::RefusedByRisk
| VoltPaymentStatus::NotReceived
| VoltPaymentStatus::ErrorAtBank
| VoltPaymentStatus::CancelledByUser
| VoltPaymentStatus::AbandonedByUser
| VoltPaymentStatus::Failed => Self::Failure,
}
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VoltPaymentsResponse {
status: VoltPaymentStatus,
checkout_url: String,
id: String,
}
@ -126,16 +269,73 @@ impl<F, T>
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, VoltPaymentsResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let url = item.response.checkout_url;
let redirection_data = Some(services::RedirectForm::Form {
endpoint: url,
method: services::Method::Get,
form_fields: Default::default(),
});
Ok(Self {
status: enums::AttemptStatus::AuthenticationPending,
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()),
redirection_data,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(item.response.id),
}),
..item.data
})
}
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum VoltPaymentStatus {
NewPayment,
Completed,
Received,
NotReceived,
BankRedirect,
DelayedAtBank,
AwaitingCheckoutAuthorisation,
RefusedByBank,
RefusedByRisk,
ErrorAtBank,
CancelledByUser,
AbandonedByUser,
Failed,
Settled,
}
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VoltPsyncResponse {
status: VoltPaymentStatus,
id: String,
merchant_internal_reference: Option<String>,
}
impl<F, T> TryFrom<types::ResponseRouterData<F, VoltPsyncResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, VoltPsyncResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
Ok(Self {
status: enums::AttemptStatus::from(item.response.status),
response: Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id),
resource_id: types::ResponseId::ConnectorTransactionId(item.response.id.clone()),
redirection_data: None,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
connector_response_reference_id: item
.response
.merchant_internal_reference
.or(Some(item.response.id)),
}),
..item.data
})
@ -147,13 +347,15 @@ impl<F, T>
#[derive(Default, Debug, Serialize)]
pub struct VoltRefundRequest {
pub amount: i64,
pub external_reference: String,
}
impl<F> TryFrom<&VoltRouterData<&types::RefundsRouterData<F>>> for VoltRefundRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &VoltRouterData<&types::RefundsRouterData<F>>) -> Result<Self, Self::Error> {
Ok(Self {
amount: item.amount.to_owned(),
amount: item.router_data.request.refund_amount,
external_reference: item.router_data.request.refund_id.clone(),
})
}
}
@ -180,10 +382,9 @@ impl From<RefundStatus> for enums::RefundStatus {
}
}
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
#[derive(Default, Debug, Clone, Deserialize)]
pub struct RefundResponse {
id: String,
status: RefundStatus,
}
impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
@ -196,24 +397,7 @@ impl TryFrom<types::RefundsResponseRouterData<api::Execute, RefundResponse>>
Ok(Self {
response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status),
}),
..item.data
})
}
}
impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
for types::RefundsRouterData<api::RSync>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::RefundsResponseRouterData<api::RSync, RefundResponse>,
) -> Result<Self, Self::Error> {
Ok(Self {
response: Ok(types::RefundsResponseData {
connector_refund_id: item.response.id.to_string(),
refund_status: enums::RefundStatus::from(item.response.status),
refund_status: enums::RefundStatus::Pending, //We get Refund Status only by Webhooks
}),
..item.data
})
@ -222,8 +406,19 @@ impl TryFrom<types::RefundsResponseRouterData<api::RSync, RefundResponse>>
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct VoltErrorResponse {
pub status_code: u16,
pub code: String,
pub message: String,
pub reason: Option<String>,
pub exception: VoltErrorException,
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct VoltErrorException {
pub code: u64,
pub message: String,
pub error_list: Option<Vec<VoltErrorList>>,
}
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct VoltErrorList {
pub property: String,
pub message: String,
}

View File

@ -1502,6 +1502,10 @@ pub(crate) fn validate_auth_and_metadata_type(
tsys::transformers::TsysAuthType::try_from(val)?;
Ok(())
}
api_enums::Connector::Volt => {
volt::transformers::VoltAuthType::try_from(val)?;
Ok(())
}
api_enums::Connector::Wise => {
wise::transformers::WiseAuthType::try_from(val)?;
Ok(())

View File

@ -958,6 +958,11 @@ impl TryFrom<ConnectorAuthType> for AccessTokenRequestData {
app_id: api_key,
id: Some(key1),
}),
ConnectorAuthType::MultiAuthKey { api_key, key1, .. } => Ok(Self {
app_id: api_key,
id: Some(key1),
}),
_ => Err(errors::ApiErrorResponse::InvalidDataValue {
field_name: "connector_account_details",
}),

View File

@ -362,7 +362,7 @@ impl ConnectorData {
enums::Connector::Paypal => Ok(Box::new(&connector::Paypal)),
enums::Connector::Trustpay => Ok(Box::new(&connector::Trustpay)),
enums::Connector::Tsys => Ok(Box::new(&connector::Tsys)),
// enums::Connector::Volt => Ok(Box::new(&connector::Volt)), it is added as template code for future usage
enums::Connector::Volt => Ok(Box::new(&connector::Volt)),
enums::Connector::Zen => Ok(Box::new(&connector::Zen)),
enums::Connector::Signifyd | enums::Connector::Plaid => {
Err(report!(errors::ConnectorError::InvalidConnectorName)