Skip to content

Commit

Permalink
inespay sepa bank debit auth and psync flow
Browse files Browse the repository at this point in the history
  • Loading branch information
ImSagnik007 committed Dec 5, 2024
1 parent b370d04 commit eb41804
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 71 deletions.
4 changes: 2 additions & 2 deletions crates/api_models/src/connector_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ pub enum Connector {
Gocardless,
Gpayments,
Helcim,
// Inespay,
Inespay,
Iatapay,
Itaubank,
//Jpmorgan,
Expand Down Expand Up @@ -230,7 +230,7 @@ impl Connector {
| Self::Gpayments
| Self::Helcim
| Self::Iatapay
// | Self::Inespay
| Self::Inespay
| Self::Itaubank
//| Self::Jpmorgan
| Self::Klarna
Expand Down
2 changes: 1 addition & 1 deletion crates/common_enums/src/connector_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub enum RoutableConnectors {
Gocardless,
Helcim,
Iatapay,
// Inespay,
Inespay,
Itaubank,
//Jpmorgan,
Klarna,
Expand Down
4 changes: 2 additions & 2 deletions crates/connector_configs/src/connector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ pub struct ConnectorConfig {
pub gocardless: Option<ConnectorTomlConfig>,
pub gpayments: Option<ConnectorTomlConfig>,
pub helcim: Option<ConnectorTomlConfig>,
// pub inespay: Option<ConnectorTomlConfig>,
pub inespay: Option<ConnectorTomlConfig>,
pub klarna: Option<ConnectorTomlConfig>,
pub mifinity: Option<ConnectorTomlConfig>,
pub mollie: Option<ConnectorTomlConfig>,
Expand Down Expand Up @@ -358,7 +358,7 @@ impl ConnectorConfig {
Connector::Gocardless => Ok(connector_data.gocardless),
Connector::Gpayments => Ok(connector_data.gpayments),
Connector::Helcim => Ok(connector_data.helcim),
// Connector::Inespay => Ok(connector_data.inespay),
Connector::Inespay => Ok(connector_data.inespay),
Connector::Klarna => Ok(connector_data.klarna),
Connector::Mifinity => Ok(connector_data.mifinity),
Connector::Mollie => Ok(connector_data.mollie),
Expand Down
50 changes: 33 additions & 17 deletions crates/hyperswitch_connectors/src/connectors/inespay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use hyperswitch_domain_models::{
use hyperswitch_interfaces::{
api::{self, ConnectorCommon, ConnectorCommonExt, ConnectorIntegration, ConnectorValidation},
configs::Connectors,
errors,
consts, errors,
events::connector_api_logs::ConnectorEvent,
types::{self, Response},
webhooks,
Expand Down Expand Up @@ -83,8 +83,8 @@ where
headers::CONTENT_TYPE.to_string(),
self.get_content_type().to_string().into(),
)];
let mut api_key = self.get_auth_header(&req.connector_auth_type)?;
header.append(&mut api_key);
let mut auth_headers = self.get_auth_header(&req.connector_auth_type)?;
header.append(&mut auth_headers);
Ok(header)
}
}
Expand All @@ -95,7 +95,7 @@ impl ConnectorCommon for Inespay {
}

fn get_currency_unit(&self) -> api::CurrencyUnit {
api::CurrencyUnit::Base
api::CurrencyUnit::Minor
// TODO! Check connector documentation, on which unit they are processing the currency.
// If the connector accepts amount in lower unit ( i.e cents for USD) then return api::CurrencyUnit::Minor,
// if connector accepts amount in base unit (i.e dollars for USD) then return api::CurrencyUnit::Base
Expand All @@ -115,10 +115,16 @@ impl ConnectorCommon for Inespay {
) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
let auth = inespay::InespayAuthType::try_from(auth_type)
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
Ok(vec![(
headers::AUTHORIZATION.to_string(),
auth.api_key.expose().into_masked(),
)])
Ok(vec![
(
headers::AUTHORIZATION.to_string(),
auth.authorization.expose().into_masked(),
),
(
headers::X_API_KEY.to_string(),
auth.api_key.expose().into_masked(),
),
])
}

fn build_error_response(
Expand All @@ -136,9 +142,9 @@ impl ConnectorCommon for Inespay {

Ok(ErrorResponse {
status_code: res.status_code,
code: response.code,
message: response.message,
reason: response.reason,
code: consts::NO_ERROR_CODE.to_string(),
message: response.message.clone(),
reason: Some(response.message.clone()),
attempt_status: None,
connector_transaction_id: None,
})
Expand Down Expand Up @@ -173,9 +179,9 @@ impl ConnectorIntegration<Authorize, PaymentsAuthorizeData, PaymentsResponseData
fn get_url(
&self,
_req: &PaymentsAuthorizeRouterData,
_connectors: &Connectors,
connectors: &Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Err(errors::ConnectorError::NotImplemented("get_url method".to_string()).into())
Ok(format!("{}/payins/single/init", self.base_url(connectors)))
}

fn get_request_body(
Expand Down Expand Up @@ -259,10 +265,20 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Ine

fn get_url(
&self,
_req: &PaymentsSyncRouterData,
_connectors: &Connectors,
req: &PaymentsSyncRouterData,
connectors: &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!(
"{}{}{}",
self.base_url(connectors),
"/payins/single/",
connector_payment_id,
))
}

fn build_request(
Expand All @@ -286,7 +302,7 @@ impl ConnectorIntegration<PSync, PaymentsSyncData, PaymentsResponseData> for Ine
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<PaymentsSyncRouterData, errors::ConnectorError> {
let response: inespay::InespayPaymentsResponse = res
let response: inespay::InespayPSyncResponse = res
.response
.parse_struct("inespay PaymentsSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
Expand Down
154 changes: 113 additions & 41 deletions crates/hyperswitch_connectors/src/connectors/inespay/transformers.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
use common_enums::enums;
use common_utils::types::StringMinorUnit;
use common_utils::{request::Method, types::StringMinorUnit};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
payment_method_data::PaymentMethodData,
payment_method_data::{BankDebitData, PaymentMethodData},
router_data::{ConnectorAuthType, RouterData},
router_flow_types::refunds::{Execute, RSync},
router_request_types::ResponseId,
router_response_types::{PaymentsResponseData, RefundsResponseData},
router_response_types::{PaymentsResponseData, RedirectForm, RefundsResponseData},
types::{PaymentsAuthorizeRouterData, RefundsRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
use serde::{Deserialize, Serialize};
use url::Url;

use crate::{
types::{RefundsResponseRouterData, ResponseRouterData},
utils::PaymentsAuthorizeRequestData,
utils::RouterData as _,
};

//TODO: Fill the struct with respective fields
Expand All @@ -35,9 +37,12 @@ impl<T> From<(StringMinorUnit, T)> for InespayRouterData<T> {

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayPaymentsRequest {
description: String,
amount: StringMinorUnit,
card: InespayCard,
reference: String,
debtor_account: Secret<String>,
}

#[derive(Default, Debug, Serialize, Eq, PartialEq)]
Expand All @@ -55,17 +60,13 @@ impl TryFrom<&InespayRouterData<&PaymentsAuthorizeRouterData>> for InespayPaymen
item: &InespayRouterData<&PaymentsAuthorizeRouterData>,
) -> Result<Self, Self::Error> {
match item.router_data.request.payment_method_data.clone() {
PaymentMethodData::Card(req_card) => {
let card = InespayCard {
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()?,
};
PaymentMethodData::BankDebit(BankDebitData::SepaBankDebit { iban, .. }) => {
let order_id = item.router_data.connector_request_reference_id.clone();
Ok(Self {
description: item.router_data.get_description()?,
amount: item.amount.clone(),
card,
reference: order_id,
debtor_account: iban,
})
}
_ => Err(errors::ConnectorError::NotImplemented("Payment method".to_string()).into()),
Expand All @@ -77,59 +78,133 @@ impl TryFrom<&InespayRouterData<&PaymentsAuthorizeRouterData>> for InespayPaymen
// Auth Struct
pub struct InespayAuthType {
pub(super) api_key: Secret<String>,
pub authorization: Secret<String>,
}

impl TryFrom<&ConnectorAuthType> for InespayAuthType {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &ConnectorAuthType) -> Result<Self, Self::Error> {
match auth_type {
ConnectorAuthType::HeaderKey { api_key } => Ok(Self {
ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
api_key: api_key.to_owned(),
authorization: key1.to_owned(),
}),
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
}
}
}
// PaymentsResponse
//TODO: Append the remaining status flags
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum InespayPaymentStatus {
Succeeded,

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayPaymentsResponse {
status: String,
status_desc: String,
single_payin_id: String,
single_payin_link: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, InespayPaymentsResponse, T, PaymentsResponseData>>
for RouterData<F, T, PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: ResponseRouterData<F, InespayPaymentsResponse, T, PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_url = Url::parse(item.response.single_payin_link.as_str())
.change_context(errors::ConnectorError::FailedToObtainIntegrationUrl)?;
let redirection_data = RedirectForm::from((redirection_url, Method::Get));
let status = match item.response.status_desc.as_str() {
"Success" => common_enums::AttemptStatus::AuthenticationPending,
_ => common_enums::AttemptStatus::Failure,
};

Ok(Self {
status,
response: Ok(PaymentsResponseData::TransactionResponse {
resource_id: ResponseId::ConnectorTransactionId(
item.response.single_payin_id.clone(),
),
redirection_data: Box::new(Some(redirection_data)),
mandate_reference: Box::new(None),
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: None,
incremental_authorization_allowed: None,
charge_id: None,
}),
..item.data
})
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum InespayPSyncStatus {
Ok,
Created,
Opened,
BankSelected,
Initiated,
Pending,
Aborted,
Unfinished,
Rejected,
Cancelled,
PartiallyAccepted,
Failed,
#[default]
Processing,
Settled,
PartRefunded,
Refunded,
}

impl From<InespayPaymentStatus> for common_enums::AttemptStatus {
fn from(item: InespayPaymentStatus) -> Self {
impl From<InespayPSyncStatus> for common_enums::AttemptStatus {
fn from(item: InespayPSyncStatus) -> Self {
match item {
InespayPaymentStatus::Succeeded => Self::Charged,
InespayPaymentStatus::Failed => Self::Failure,
InespayPaymentStatus::Processing => Self::Authorizing,
InespayPSyncStatus::Ok | InespayPSyncStatus::Settled => Self::Charged,
InespayPSyncStatus::Created
| InespayPSyncStatus::Opened
| InespayPSyncStatus::BankSelected
| InespayPSyncStatus::Initiated
| InespayPSyncStatus::Pending
| InespayPSyncStatus::Unfinished
| InespayPSyncStatus::PartiallyAccepted => Self::AuthenticationPending,
InespayPSyncStatus::Aborted
| InespayPSyncStatus::Rejected
| InespayPSyncStatus::Cancelled
| InespayPSyncStatus::Failed => Self::Failure,
InespayPSyncStatus::PartRefunded | InespayPSyncStatus::Refunded => Self::AutoRefunded,
}
}
}

//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct InespayPaymentsResponse {
status: InespayPaymentStatus,
id: String,
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct InespayPSyncResponse {
cod_status: InespayPSyncStatus,
status_desc: String,
single_payin_id: String,
single_payin_link: String,
}

impl<F, T> TryFrom<ResponseRouterData<F, InespayPaymentsResponse, T, PaymentsResponseData>>
impl<F, T> TryFrom<ResponseRouterData<F, InespayPSyncResponse, T, PaymentsResponseData>>
for RouterData<F, T, PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: ResponseRouterData<F, InespayPaymentsResponse, T, PaymentsResponseData>,
item: ResponseRouterData<F, InespayPSyncResponse, T, PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let redirection_url = Url::parse(item.response.single_payin_link.as_str())
.change_context(errors::ConnectorError::FailedToObtainIntegrationUrl)?;
let redirection_data = RedirectForm::from((redirection_url, Method::Get));

Ok(Self {
status: common_enums::AttemptStatus::from(item.response.status),
status: common_enums::AttemptStatus::from(item.response.cod_status),
response: Ok(PaymentsResponseData::TransactionResponse {
resource_id: ResponseId::ConnectorTransactionId(item.response.id),
redirection_data: Box::new(None),
resource_id: ResponseId::ConnectorTransactionId(
item.response.single_payin_id.clone(),
),
redirection_data: Box::new(Some(redirection_data)),
mandate_reference: Box::new(None),
connector_metadata: None,
network_txn_id: None,
Expand Down Expand Up @@ -221,8 +296,5 @@ impl TryFrom<RefundsResponseRouterData<RSync, RefundResponse>> for RefundsRouter
//TODO: Fill the struct with respective fields
#[derive(Default, Debug, Serialize, Deserialize, PartialEq)]
pub struct InespayErrorResponse {
pub status_code: u16,
pub code: String,
pub message: String,
pub reason: Option<String>,
}
8 changes: 4 additions & 4 deletions crates/router/src/core/admin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1393,10 +1393,10 @@ impl ConnectorAuthTypeAndMetadataValidation<'_> {
iatapay::transformers::IatapayAuthType::try_from(self.auth_type)?;
Ok(())
}
// api_enums::Connector::Inespay => {
// inespay::transformers::InespayAuthType::try_from(self.auth_type)?;
// Ok(())
// }
api_enums::Connector::Inespay => {
inespay::transformers::InespayAuthType::try_from(self.auth_type)?;
Ok(())
}
api_enums::Connector::Itaubank => {
itaubank::transformers::ItaubankAuthType::try_from(self.auth_type)?;
Ok(())
Expand Down
Loading

0 comments on commit eb41804

Please sign in to comment.