feat(connector): Plaid connector Integration (#3952)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
This commit is contained in:
Sarthak Soni
2024-07-19 13:35:27 +05:30
committed by GitHub
parent 33298b3808
commit eb01680284
26 changed files with 1403 additions and 56 deletions

View File

@ -47,6 +47,7 @@ pub mod payone;
pub mod paypal;
pub mod payu;
pub mod placetopay;
pub mod plaid;
pub mod powertranz;
pub mod prophetpay;
pub mod rapyd;
@ -81,7 +82,7 @@ pub use self::{
iatapay::Iatapay, itaubank::Itaubank, klarna::Klarna, mifinity::Mifinity, mollie::Mollie,
multisafepay::Multisafepay, netcetera::Netcetera, nexinets::Nexinets, nmi::Nmi, noon::Noon,
nuvei::Nuvei, opayo::Opayo, opennode::Opennode, payeezy::Payeezy, payme::Payme, payone::Payone,
paypal::Paypal, payu::Payu, placetopay::Placetopay, powertranz::Powertranz,
paypal::Paypal, payu::Payu, placetopay::Placetopay, plaid::Plaid, powertranz::Powertranz,
prophetpay::Prophetpay, rapyd::Rapyd, razorpay::Razorpay, riskified::Riskified, shift4::Shift4,
signifyd::Signifyd, square::Square, stax::Stax, stripe::Stripe, threedsecureio::Threedsecureio,
trustpay::Trustpay, tsys::Tsys, volt::Volt, wise::Wise, worldline::Worldline,

View File

@ -0,0 +1,458 @@
pub mod transformers;
use common_utils::types::{AmountConvertor, FloatMajorUnit, FloatMajorUnitForConnector};
use error_stack::ResultExt;
use transformers as plaid;
use crate::{
configs::settings,
connector::utils as connector_utils,
core::errors::{self, CustomResult},
events::connector_api_logs::ConnectorEvent,
headers,
services::{
self,
request::{self, Mask},
ConnectorIntegration, ConnectorValidation,
},
types::{
self,
api::{self, ConnectorCommon, ConnectorCommonExt},
ErrorResponse, RequestContent, Response,
},
utils::BytesExt,
};
#[derive(Clone)]
pub struct Plaid {
amount_converter: &'static (dyn AmountConvertor<Output = FloatMajorUnit> + Sync),
}
impl Plaid {
pub fn new() -> &'static Self {
&Self {
amount_converter: &FloatMajorUnitForConnector,
}
}
}
impl api::Payment for Plaid {}
impl api::PaymentSession for Plaid {}
impl api::ConnectorAccessToken for Plaid {}
impl api::MandateSetup for Plaid {}
impl api::PaymentAuthorize for Plaid {}
impl api::PaymentSync for Plaid {}
impl api::PaymentCapture for Plaid {}
impl api::PaymentVoid for Plaid {}
impl api::Refund for Plaid {}
impl api::RefundExecute for Plaid {}
impl api::RefundSync for Plaid {}
impl api::PaymentToken for Plaid {}
impl api::PaymentsPostProcessing for Plaid {}
impl
ConnectorIntegration<
api::PaymentMethodToken,
types::PaymentMethodTokenizationData,
types::PaymentsResponseData,
> for Plaid
{
// Not Implemented (R)
}
impl<Flow, Request, Response> ConnectorCommonExt<Flow, Request, Response> for Plaid
where
Self: ConnectorIntegration<Flow, Request, Response>,
{
fn build_headers(
&self,
req: &types::RouterData<Flow, Request, Response>,
_connectors: &settings::Connectors,
) -> CustomResult<Vec<(String, request::Maskable<String>)>, errors::ConnectorError> {
let mut header = vec![(
headers::CONTENT_TYPE.to_string(),
self.get_content_type().to_string().into(),
)];
let mut auth = self.get_auth_header(&req.connector_auth_type)?;
header.append(&mut auth);
Ok(header)
}
}
impl ConnectorCommon for Plaid {
fn id(&self) -> &'static str {
"plaid"
}
fn get_currency_unit(&self) -> api::CurrencyUnit {
api::CurrencyUnit::Base
}
fn common_get_content_type(&self) -> &'static str {
"application/json"
}
fn base_url<'a>(&self, connectors: &'a settings::Connectors) -> &'a str {
connectors.plaid.base_url.as_ref()
}
fn get_auth_header(
&self,
auth_type: &types::ConnectorAuthType,
) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
let auth = plaid::PlaidAuthType::try_from(auth_type)
.change_context(errors::ConnectorError::FailedToObtainAuthType)?;
let client_id = auth.client_id.into_masked();
let secret = auth.secret.into_masked();
Ok(vec![
("PLAID-CLIENT-ID".to_string(), client_id),
("PLAID-SECRET".to_string(), secret),
])
}
fn build_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
let response: plaid::PlaidErrorResponse =
res.response
.parse_struct("PlaidErrorResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);
Ok(ErrorResponse {
status_code: res.status_code,
code: response
.error_code
.unwrap_or(crate::consts::NO_ERROR_CODE.to_string()),
message: response.error_message,
reason: response.display_message,
attempt_status: None,
connector_transaction_id: None,
})
}
}
impl ConnectorValidation for Plaid {
//TODO: implement functions when support enabled
}
impl ConnectorIntegration<api::Session, types::PaymentsSessionData, types::PaymentsResponseData>
for Plaid
{
//TODO: implement sessions flow
}
impl ConnectorIntegration<api::AccessTokenAuth, types::AccessTokenRequestData, types::AccessToken>
for Plaid
{
}
impl
ConnectorIntegration<
api::SetupMandate,
types::SetupMandateRequestData,
types::PaymentsResponseData,
> for Plaid
{
}
impl ConnectorIntegration<api::Authorize, types::PaymentsAuthorizeData, types::PaymentsResponseData>
for Plaid
{
fn get_headers(
&self,
req: &types::PaymentsAuthorizeRouterData,
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::PaymentsAuthorizeRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!(
"{}/payment_initiation/payment/create",
self.base_url(connectors)
))
}
fn get_request_body(
&self,
req: &types::PaymentsAuthorizeRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let amount = connector_utils::convert_amount(
self.amount_converter,
req.request.minor_amount,
req.request.currency,
)?;
let connector_router_data = plaid::PlaidRouterData::from((amount, req));
let connector_req = plaid::PlaidPaymentsRequest::try_from(&connector_router_data)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
fn build_request(
&self,
req: &types::PaymentsAuthorizeRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PaymentsAuthorizeType::get_url(
self, req, connectors,
)?)
.attach_default_headers()
.headers(types::PaymentsAuthorizeType::get_headers(
self, req, connectors,
)?)
.set_body(types::PaymentsAuthorizeType::get_request_body(
self, req, connectors,
)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::PaymentsAuthorizeRouterData,
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<types::PaymentsAuthorizeRouterData, errors::ConnectorError> {
let response: plaid::PlaidPaymentsResponse = res
.response
.parse_struct("PlaidPaymentsResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
}
impl ConnectorIntegration<api::PSync, types::PaymentsSyncData, types::PaymentsResponseData>
for Plaid
{
fn get_headers(
&self,
req: &types::PaymentsSyncRouterData,
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_request_body(
&self,
req: &types::PaymentsSyncRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_req = plaid::PlaidSyncRequest::try_from(req)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
fn get_url(
&self,
_req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!(
"{}/payment_initiation/payment/get",
self.base_url(connectors)
))
}
fn build_request(
&self,
req: &types::PaymentsSyncRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PaymentsSyncType::get_url(self, req, connectors)?)
.attach_default_headers()
.headers(types::PaymentsSyncType::get_headers(self, req, connectors)?)
.set_body(types::PaymentsSyncType::get_request_body(
self, req, connectors,
)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::PaymentsSyncRouterData,
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<types::PaymentsSyncRouterData, errors::ConnectorError> {
let response: plaid::PlaidSyncResponse = res
.response
.parse_struct("PlaidSyncResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
}
impl
ConnectorIntegration<
api::PostProcessing,
types::PaymentsPostProcessingData,
types::PaymentsResponseData,
> for Plaid
{
fn get_headers(
&self,
req: &types::PaymentsPostProcessingRouterData,
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::PaymentsPostProcessingRouterData,
connectors: &settings::Connectors,
) -> CustomResult<String, errors::ConnectorError> {
Ok(format!("{}/link/token/create", self.base_url(connectors)))
}
fn get_request_body(
&self,
req: &types::PaymentsPostProcessingRouterData,
_connectors: &settings::Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_req = plaid::PlaidLinkTokenRequest::try_from(req)?;
Ok(RequestContent::Json(Box::new(connector_req)))
}
fn build_request(
&self,
req: &types::PaymentsPostProcessingRouterData,
connectors: &settings::Connectors,
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
Ok(Some(
services::RequestBuilder::new()
.method(services::Method::Post)
.url(&types::PaymentsPostProcessingType::get_url(
self, req, connectors,
)?)
.attach_default_headers()
.headers(types::PaymentsPostProcessingType::get_headers(
self, req, connectors,
)?)
.set_body(types::PaymentsPostProcessingType::get_request_body(
self, req, connectors,
)?)
.build(),
))
}
fn handle_response(
&self,
data: &types::PaymentsPostProcessingRouterData,
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<types::PaymentsPostProcessingRouterData, errors::ConnectorError> {
let response: plaid::PlaidLinkTokenResponse = res
.response
.parse_struct("PlaidLinkTokenResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);
types::RouterData::try_from(types::ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
}
impl ConnectorIntegration<api::Capture, types::PaymentsCaptureData, types::PaymentsResponseData>
for Plaid
{
}
impl ConnectorIntegration<api::Void, types::PaymentsCancelData, types::PaymentsResponseData>
for Plaid
{
}
impl ConnectorIntegration<api::Execute, types::RefundsData, types::RefundsResponseData> for Plaid {}
impl ConnectorIntegration<api::RSync, types::RefundsData, types::RefundsResponseData> for Plaid {}
#[async_trait::async_trait]
impl api::IncomingWebhook for Plaid {
fn get_webhook_object_reference_id(
&self,
_request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api::webhooks::ObjectReferenceId, errors::ConnectorError> {
Err((errors::ConnectorError::WebhooksNotImplemented).into())
}
fn get_webhook_event_type(
&self,
_request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<api::IncomingWebhookEvent, errors::ConnectorError> {
Err((errors::ConnectorError::WebhooksNotImplemented).into())
}
fn get_webhook_resource_object(
&self,
_request: &api::IncomingWebhookRequestDetails<'_>,
) -> CustomResult<Box<dyn masking::ErasedMaskSerialize>, errors::ConnectorError> {
Err((errors::ConnectorError::WebhooksNotImplemented).into())
}
}

View File

@ -0,0 +1,389 @@
use common_enums::Currency;
use common_utils::types::FloatMajorUnit;
use error_stack::ResultExt;
use masking::{PeekInterface, Secret};
use serde::{Deserialize, Serialize};
use crate::{
connector::utils::is_payment_failure,
core::errors,
types::{self, api, domain, storage::enums},
};
pub struct PlaidRouterData<T> {
pub amount: FloatMajorUnit,
pub router_data: T,
}
impl<T> From<(FloatMajorUnit, T)> for PlaidRouterData<T> {
fn from((amount, item): (FloatMajorUnit, T)) -> Self {
Self {
amount,
router_data: item,
}
}
}
#[derive(Default, Debug, Serialize)]
pub struct PlaidPaymentsRequest {
amount: PlaidAmount,
recipient_id: String,
reference: String,
#[serde(skip_serializing_if = "Option::is_none")]
schedule: Option<PlaidSchedule>,
#[serde(skip_serializing_if = "Option::is_none")]
options: Option<PlaidOptions>,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct PlaidAmount {
currency: Currency,
value: FloatMajorUnit,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct PlaidSchedule {
interval: String,
interval_execution_day: String,
start_date: String,
end_date: Option<String>,
adjusted_start_date: Option<String>,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct PlaidOptions {
request_refund_details: bool,
iban: Option<Secret<String>>,
bacs: Option<PlaidBacs>,
scheme: String,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct PlaidBacs {
account: Secret<String>,
sort_code: Secret<String>,
}
#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub struct PlaidLinkTokenRequest {
client_name: String,
country_codes: Vec<String>,
language: String,
products: Vec<String>,
user: User,
payment_initiation: PlaidPaymentInitiation,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct User {
pub client_user_id: String,
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct PlaidPaymentInitiation {
payment_id: String,
}
impl TryFrom<&PlaidRouterData<&types::PaymentsAuthorizeRouterData>> for PlaidPaymentsRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: &PlaidRouterData<&types::PaymentsAuthorizeRouterData>,
) -> Result<Self, Self::Error> {
match item.router_data.request.payment_method_data.clone() {
domain::PaymentMethodData::OpenBanking(ref data) => match data {
domain::OpenBankingData::OpenBankingPIS { .. } => {
let amount = item.amount;
let currency = item.router_data.request.currency;
let payment_id = item.router_data.payment_id.clone();
let id_len = payment_id.len();
let reference = if id_len > 18 {
payment_id.get(id_len - 18..id_len).map(|id| id.to_string())
} else {
Some(payment_id)
}
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "payment_id",
})?;
let recipient_val = item
.router_data
.connector_meta_data
.as_ref()
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "connector_customer",
})?
.peek()
.clone();
let recipient_type =
serde_json::from_value::<types::MerchantRecipientData>(recipient_val)
.change_context(errors::ConnectorError::ParsingFailed)?;
let recipient_id = match recipient_type {
types::MerchantRecipientData::ConnectorRecipientId(id) => {
Ok(id.peek().to_string())
}
_ => Err(errors::ConnectorError::MissingRequiredField {
field_name: "ConnectorRecipientId",
}),
}?;
Ok(Self {
amount: PlaidAmount {
currency,
value: amount,
},
reference,
recipient_id,
schedule: None,
options: None,
})
}
},
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
}
}
}
impl TryFrom<&types::PaymentsSyncRouterData> for PlaidSyncRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsSyncRouterData) -> Result<Self, Self::Error> {
match item.request.connector_transaction_id {
types::ResponseId::ConnectorTransactionId(ref id) => Ok(Self {
payment_id: id.clone(),
}),
_ => Err((errors::ConnectorError::MissingConnectorTransactionID).into()),
}
}
}
impl TryFrom<&types::PaymentsPostProcessingRouterData> for PlaidLinkTokenRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &types::PaymentsPostProcessingRouterData) -> Result<Self, Self::Error> {
match item.request.payment_method_data.clone() {
domain::PaymentMethodData::OpenBanking(ref data) => match data {
domain::OpenBankingData::OpenBankingPIS { .. } => Ok(Self {
client_name: "Hyperswitch".to_string(),
country_codes: item
.request
.country
.map(|code| vec![code.to_string()])
.ok_or(errors::ConnectorError::MissingRequiredField {
field_name: "billing.address.country",
})?,
language: "en".to_string(),
products: vec!["payment_initiation".to_string()],
user: User {
client_user_id: item
.request
.customer_id
.clone()
.map(|id| id.get_string_repr().to_string())
.unwrap_or("default cust".to_string()),
},
payment_initiation: PlaidPaymentInitiation {
payment_id: item
.request
.connector_transaction_id
.clone()
.ok_or(errors::ConnectorError::MissingConnectorTransactionID)?,
},
}),
},
_ => Err(errors::ConnectorError::NotImplemented("Payment methods".to_string()).into()),
}
}
}
pub struct PlaidAuthType {
pub client_id: Secret<String>,
pub secret: Secret<String>,
}
impl TryFrom<&types::ConnectorAuthType> for PlaidAuthType {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(auth_type: &types::ConnectorAuthType) -> Result<Self, Self::Error> {
match auth_type {
types::ConnectorAuthType::BodyKey { api_key, key1 } => Ok(Self {
client_id: api_key.to_owned(),
secret: key1.to_owned(),
}),
_ => Err(errors::ConnectorError::FailedToObtainAuthType.into()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(strum::Display)]
pub enum PlaidPaymentStatus {
PaymentStatusInputNeeded,
PaymentStatusInitiated,
PaymentStatusInsufficientFunds,
PaymentStatusFailed,
PaymentStatusBlocked,
PaymentStatusCancelled,
PaymentStatusExecuted,
PaymentStatusSettled,
PaymentStatusEstablished,
PaymentStatusRejected,
PaymentStatusAuthorising,
}
impl From<PlaidPaymentStatus> for enums::AttemptStatus {
fn from(item: PlaidPaymentStatus) -> Self {
match item {
PlaidPaymentStatus::PaymentStatusAuthorising => Self::Authorizing,
PlaidPaymentStatus::PaymentStatusBlocked => Self::AuthorizationFailed,
PlaidPaymentStatus::PaymentStatusCancelled => Self::Voided,
PlaidPaymentStatus::PaymentStatusEstablished => Self::Authorized,
PlaidPaymentStatus::PaymentStatusExecuted => Self::Authorized,
PlaidPaymentStatus::PaymentStatusFailed => Self::Failure,
PlaidPaymentStatus::PaymentStatusInitiated => Self::AuthenticationPending,
PlaidPaymentStatus::PaymentStatusInputNeeded => Self::AuthenticationPending,
PlaidPaymentStatus::PaymentStatusInsufficientFunds => Self::AuthorizationFailed,
PlaidPaymentStatus::PaymentStatusRejected => Self::AuthorizationFailed,
PlaidPaymentStatus::PaymentStatusSettled => Self::Charged,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PlaidPaymentsResponse {
status: PlaidPaymentStatus,
payment_id: String,
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, PlaidPaymentsResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, PlaidPaymentsResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let status = enums::AttemptStatus::from(item.response.status.clone());
Ok(Self {
status,
response: if is_payment_failure(status) {
Err(types::ErrorResponse {
// populating status everywhere as plaid only sends back a status
code: item.response.status.clone().to_string(),
message: item.response.status.clone().to_string(),
reason: Some(item.response.status.to_string()),
status_code: item.http_code,
attempt_status: None,
connector_transaction_id: Some(item.response.payment_id),
})
} else {
Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(
item.response.payment_id.clone(),
),
redirection_data: None,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(item.response.payment_id),
incremental_authorization_allowed: None,
charge_id: None,
})
},
..item.data
})
}
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct PlaidLinkTokenResponse {
link_token: String,
}
impl<F, T>
TryFrom<types::ResponseRouterData<F, PlaidLinkTokenResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, PlaidLinkTokenResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let session_token = Some(api::OpenBankingSessionToken {
open_banking_session_token: item.response.link_token,
});
Ok(Self {
status: enums::AttemptStatus::AuthenticationPending,
response: Ok(types::PaymentsResponseData::PostProcessingResponse { session_token }),
..item.data
})
}
}
#[derive(Default, Debug, Serialize, Deserialize)]
pub struct PlaidSyncRequest {
payment_id: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PlaidSyncResponse {
payment_id: String,
amount: PlaidAmount,
status: PlaidPaymentStatus,
recipient_id: String,
reference: String,
last_status_update: String,
adjusted_reference: Option<String>,
schedule: Option<PlaidSchedule>,
iban: Option<Secret<String>>,
bacs: Option<PlaidBacs>,
scheme: Option<String>,
adjusted_scheme: Option<String>,
request_id: String,
}
impl<F, T> TryFrom<types::ResponseRouterData<F, PlaidSyncResponse, T, types::PaymentsResponseData>>
for types::RouterData<F, T, types::PaymentsResponseData>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: types::ResponseRouterData<F, PlaidSyncResponse, T, types::PaymentsResponseData>,
) -> Result<Self, Self::Error> {
let status = enums::AttemptStatus::from(item.response.status.clone());
Ok(Self {
status,
response: if is_payment_failure(status) {
Err(types::ErrorResponse {
// populating status everywhere as plaid only sends back a status
code: item.response.status.clone().to_string(),
message: item.response.status.clone().to_string(),
reason: Some(item.response.status.to_string()),
status_code: item.http_code,
attempt_status: None,
connector_transaction_id: Some(item.response.payment_id),
})
} else {
Ok(types::PaymentsResponseData::TransactionResponse {
resource_id: types::ResponseId::ConnectorTransactionId(
item.response.payment_id.clone(),
),
redirection_data: None,
mandate_reference: None,
connector_metadata: None,
network_txn_id: None,
connector_response_reference_id: Some(item.response.payment_id),
incremental_authorization_allowed: None,
charge_id: None,
})
},
..item.data
})
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub struct PlaidErrorResponse {
pub display_message: Option<String>,
pub error_code: Option<String>,
pub error_message: String,
pub error_type: Option<String>,
}

View File

@ -1256,6 +1256,8 @@ pub async fn create_payment_connector(
expected_format: "auth_type and api_key".to_string(),
})?;
validate_auth_and_metadata_type(req.connector_name, &auth, &req.metadata)?;
let merchant_recipient_data = if let Some(data) = &req.additional_merchant_data {
Some(
process_open_banking_connectors(
@ -1280,8 +1282,6 @@ pub async fn create_payment_connector(
.change_context(errors::ApiErrorResponse::InternalServerError)
.attach_printable("Failed to get MerchantRecipientData")?;
validate_auth_and_metadata_type(req.connector_name, &auth, &req.metadata)?;
let frm_configs = get_frm_config_as_secret(req.frm_configs);
// The purpose of this merchant account update is just to update the

View File

@ -699,7 +699,8 @@ default_imp_for_new_connector_integration_payment!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_refund {
@ -784,7 +785,8 @@ default_imp_for_new_connector_integration_refund!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_connector_access_token {
@ -864,7 +866,8 @@ default_imp_for_new_connector_integration_connector_access_token!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_accept_dispute {
@ -966,7 +969,8 @@ default_imp_for_new_connector_integration_accept_dispute!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_defend_dispute {
@ -1050,7 +1054,8 @@ default_imp_for_new_connector_integration_defend_dispute!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
default_imp_for_new_connector_integration_submit_evidence!(
connector::Aci,
@ -1118,7 +1123,8 @@ default_imp_for_new_connector_integration_submit_evidence!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_file_upload {
@ -1213,7 +1219,8 @@ default_imp_for_new_connector_integration_file_upload!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_payouts {
@ -1290,7 +1297,8 @@ default_imp_for_new_connector_integration_payouts!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1377,7 +1385,8 @@ default_imp_for_new_connector_integration_payouts_create!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1464,7 +1473,8 @@ default_imp_for_new_connector_integration_payouts_eligibility!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1551,7 +1561,8 @@ default_imp_for_new_connector_integration_payouts_fulfill!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1638,7 +1649,8 @@ default_imp_for_new_connector_integration_payouts_cancel!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1725,7 +1737,8 @@ default_imp_for_new_connector_integration_payouts_quote!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1812,7 +1825,8 @@ default_imp_for_new_connector_integration_payouts_recipient!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1899,7 +1913,8 @@ default_imp_for_new_connector_integration_payouts_sync!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "payouts")]
@ -1986,7 +2001,8 @@ default_imp_for_new_connector_integration_payouts_recipient_account!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_webhook_source_verification {
@ -2071,7 +2087,8 @@ default_imp_for_new_connector_integration_webhook_source_verification!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_frm {
@ -2148,7 +2165,8 @@ default_imp_for_new_connector_integration_frm!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "frm")]
@ -2235,7 +2253,8 @@ default_imp_for_new_connector_integration_frm_sale!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "frm")]
@ -2322,7 +2341,8 @@ default_imp_for_new_connector_integration_frm_checkout!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "frm")]
@ -2409,7 +2429,8 @@ default_imp_for_new_connector_integration_frm_transaction!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "frm")]
@ -2496,7 +2517,8 @@ default_imp_for_new_connector_integration_frm_fulfillment!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
#[cfg(feature = "frm")]
@ -2583,7 +2605,8 @@ default_imp_for_new_connector_integration_frm_record_return!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_revoking_mandates {
@ -2667,7 +2690,8 @@ default_imp_for_new_connector_integration_revoking_mandates!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);
macro_rules! default_imp_for_new_connector_integration_connector_authentication {
@ -2779,5 +2803,6 @@ default_imp_for_new_connector_integration_connector_authentication!(
connector::Worldline,
connector::Worldpay,
connector::Zen,
connector::Zsl
connector::Zsl,
connector::Plaid
);

View File

@ -221,6 +221,7 @@ default_imp_for_complete_authorize!(
connector::Payone,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Rapyd,
connector::Razorpay,
connector::Riskified,
@ -311,6 +312,7 @@ default_imp_for_webhook_source_verification!(
connector::Payone,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -406,6 +408,7 @@ default_imp_for_create_customer!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -489,6 +492,7 @@ default_imp_for_connector_redirect_response!(
connector::Payone,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -565,6 +569,7 @@ default_imp_for_connector_request_id!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -662,6 +667,7 @@ default_imp_for_accept_dispute!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -778,6 +784,7 @@ default_imp_for_file_upload!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -872,6 +879,7 @@ default_imp_for_submit_evidence!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -966,6 +974,7 @@ default_imp_for_defend_dispute!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1070,6 +1079,7 @@ default_imp_for_pre_processing_steps!(
connector::Payone,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1222,6 +1232,7 @@ default_imp_for_payouts!(
connector::Payme,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1314,6 +1325,7 @@ default_imp_for_payouts_create!(
connector::Payone,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1408,6 +1420,7 @@ default_imp_for_payouts_retrieve!(
connector::Payone,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1506,6 +1519,7 @@ default_imp_for_payouts_eligibility!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1596,6 +1610,7 @@ default_imp_for_payouts_fulfill!(
connector::Payme,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1689,6 +1704,7 @@ default_imp_for_payouts_cancel!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1783,6 +1799,7 @@ default_imp_for_payouts_quote!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1878,6 +1895,7 @@ default_imp_for_payouts_recipient!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -1976,6 +1994,7 @@ default_imp_for_payouts_recipient_account!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2071,6 +2090,7 @@ default_imp_for_approve!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2167,6 +2187,7 @@ default_imp_for_reject!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2247,6 +2268,7 @@ default_imp_for_fraud_check!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2343,6 +2365,7 @@ default_imp_for_frm_sale!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2439,6 +2462,7 @@ default_imp_for_frm_checkout!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2535,6 +2559,7 @@ default_imp_for_frm_transaction!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2631,6 +2656,7 @@ default_imp_for_frm_fulfillment!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2727,6 +2753,7 @@ default_imp_for_frm_record_return!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2820,6 +2847,7 @@ default_imp_for_incremental_authorization!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -2912,6 +2940,7 @@ default_imp_for_revoking_mandates!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -3065,6 +3094,7 @@ default_imp_for_connector_authentication!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,
@ -3156,6 +3186,7 @@ default_imp_for_authorize_session_token!(
connector::Paypal,
connector::Payu,
connector::Placetopay,
connector::Plaid,
connector::Powertranz,
connector::Prophetpay,
connector::Rapyd,

View File

@ -210,7 +210,6 @@ impl ForeignTryFrom<&types::ConnectorAuthType> for PlaidAuthType {
Ok::<Self, errors::ConnectorError>(Self {
client_id: api_key.to_owned(),
secret: key1.to_owned(),
merchant_data: None,
})
}
_ => Err(errors::ConnectorError::FailedToObtainAuthType),

View File

@ -500,8 +500,10 @@ impl ConnectorData {
enums::Connector::Volt => Ok(ConnectorEnum::Old(Box::new(&connector::Volt))),
enums::Connector::Zen => Ok(ConnectorEnum::Old(Box::new(&connector::Zen))),
enums::Connector::Zsl => Ok(ConnectorEnum::Old(Box::new(&connector::Zsl))),
enums::Connector::Plaid => {
Ok(ConnectorEnum::Old(Box::new(connector::Plaid::new())))
}
enums::Connector::Signifyd
| enums::Connector::Plaid
| enums::Connector::Riskified
| enums::Connector::Gpayments
| enums::Connector::Threedsecureio => {

View File

@ -4,8 +4,8 @@ pub use hyperswitch_domain_models::payment_method_data::{
CardToken, CashappQr, CryptoData, GcashRedirection, GiftCardData, GiftCardDetails,
GoPayRedirection, GooglePayPaymentMethodInfo, GooglePayRedirectData,
GooglePayThirdPartySdkData, GooglePayWalletData, GpayTokenizationData, IndomaretVoucherData,
KakaoPayRedirection, MbWayRedirection, MifinityData, PayLaterData, PaymentMethodData,
RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails, SwishQrData,
TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData, WalletData,
WeChatPayQr,
KakaoPayRedirection, MbWayRedirection, MifinityData, OpenBankingData, PayLaterData,
PaymentMethodData, RealTimePaymentData, SamsungPayWalletData, SepaAndBacsBillingDetails,
SwishQrData, TouchNGoRedirection, UpiCollectData, UpiData, UpiIntentData, VoucherData,
WalletData, WeChatPayQr,
};

View File

@ -274,11 +274,7 @@ impl ForeignTryFrom<api_enums::Connector> for common_enums::RoutableConnectors {
api_enums::Connector::Paypal => Self::Paypal,
api_enums::Connector::Payu => Self::Payu,
api_models::enums::Connector::Placetopay => Self::Placetopay,
api_enums::Connector::Plaid => {
Err(common_utils::errors::ValidationError::InvalidValue {
message: "plaid is not a routable connector".to_string(),
})?
}
api_enums::Connector::Plaid => Self::Plaid,
api_enums::Connector::Powertranz => Self::Powertranz,
api_enums::Connector::Prophetpay => Self::Prophetpay,
api_enums::Connector::Rapyd => Self::Rapyd,