mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-02 12:06:56 +08:00
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:
@ -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]
|
||||
|
||||
@ -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,
|
||||
}
|
||||
|
||||
@ -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(())
|
||||
|
||||
@ -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",
|
||||
}),
|
||||
|
||||
@ -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)
|
||||
|
||||
Reference in New Issue
Block a user