mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-11-01 19:42:27 +08:00
feat(router): added retrieval flow for connector file uploads and added support for stripe connector (#990)
This commit is contained in:
committed by
GitHub
parent
d6e71b959d
commit
38aa9eab3f
@ -313,3 +313,25 @@ pub enum Country {
|
|||||||
Zambia,
|
Zambia,
|
||||||
Zimbabwe,
|
Zimbabwe,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Copy,
|
||||||
|
Debug,
|
||||||
|
Eq,
|
||||||
|
PartialEq,
|
||||||
|
Default,
|
||||||
|
serde::Deserialize,
|
||||||
|
serde::Serialize,
|
||||||
|
strum::Display,
|
||||||
|
strum::EnumString,
|
||||||
|
)]
|
||||||
|
#[router_derive::diesel_enum(storage_type = "text")]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[strum(serialize_all = "snake_case")]
|
||||||
|
pub enum FileUploadProvider {
|
||||||
|
#[default]
|
||||||
|
Router,
|
||||||
|
Stripe,
|
||||||
|
Checkout,
|
||||||
|
}
|
||||||
|
|||||||
@ -118,6 +118,7 @@ impl api::ConnectorAccessToken for Checkout {}
|
|||||||
impl api::AcceptDispute for Checkout {}
|
impl api::AcceptDispute for Checkout {}
|
||||||
impl api::PaymentToken for Checkout {}
|
impl api::PaymentToken for Checkout {}
|
||||||
impl api::Dispute for Checkout {}
|
impl api::Dispute for Checkout {}
|
||||||
|
impl api::RetrieveFile for Checkout {}
|
||||||
impl api::DefendDispute for Checkout {}
|
impl api::DefendDispute for Checkout {}
|
||||||
|
|
||||||
impl
|
impl
|
||||||
@ -773,6 +774,12 @@ impl
|
|||||||
|
|
||||||
impl api::UploadFile for Checkout {}
|
impl api::UploadFile for Checkout {}
|
||||||
|
|
||||||
|
impl
|
||||||
|
ConnectorIntegration<api::Retrieve, types::RetrieveFileRequestData, types::RetrieveFileResponse>
|
||||||
|
for Checkout
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl api::FileUpload for Checkout {
|
impl api::FileUpload for Checkout {
|
||||||
fn validate_file_upload(
|
fn validate_file_upload(
|
||||||
|
|||||||
@ -1199,6 +1199,98 @@ impl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl api::RetrieveFile for Stripe {}
|
||||||
|
|
||||||
|
impl
|
||||||
|
services::ConnectorIntegration<
|
||||||
|
api::Retrieve,
|
||||||
|
types::RetrieveFileRequestData,
|
||||||
|
types::RetrieveFileResponse,
|
||||||
|
> for Stripe
|
||||||
|
{
|
||||||
|
fn get_headers(
|
||||||
|
&self,
|
||||||
|
req: &types::RouterData<
|
||||||
|
api::Retrieve,
|
||||||
|
types::RetrieveFileRequestData,
|
||||||
|
types::RetrieveFileResponse,
|
||||||
|
>,
|
||||||
|
_connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Vec<(String, String)>, errors::ConnectorError> {
|
||||||
|
self.get_auth_header(&req.connector_auth_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_url(
|
||||||
|
&self,
|
||||||
|
req: &types::RetrieveFileRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<String, errors::ConnectorError> {
|
||||||
|
Ok(format!(
|
||||||
|
"{}v1/files/{}/contents",
|
||||||
|
connectors.stripe.base_url_file_upload, req.request.provider_file_id
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_request(
|
||||||
|
&self,
|
||||||
|
req: &types::RetrieveFileRouterData,
|
||||||
|
connectors: &settings::Connectors,
|
||||||
|
) -> CustomResult<Option<services::Request>, errors::ConnectorError> {
|
||||||
|
Ok(Some(
|
||||||
|
services::RequestBuilder::new()
|
||||||
|
.method(services::Method::Get)
|
||||||
|
.url(&types::RetrieveFileType::get_url(self, req, connectors)?)
|
||||||
|
.attach_default_headers()
|
||||||
|
.headers(types::RetrieveFileType::get_headers(self, req, connectors)?)
|
||||||
|
.build(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
fn handle_response(
|
||||||
|
&self,
|
||||||
|
data: &types::RetrieveFileRouterData,
|
||||||
|
res: types::Response,
|
||||||
|
) -> CustomResult<
|
||||||
|
types::RouterData<
|
||||||
|
api::Retrieve,
|
||||||
|
types::RetrieveFileRequestData,
|
||||||
|
types::RetrieveFileResponse,
|
||||||
|
>,
|
||||||
|
errors::ConnectorError,
|
||||||
|
> {
|
||||||
|
let response = res.response;
|
||||||
|
Ok(types::RetrieveFileRouterData {
|
||||||
|
response: Ok(types::RetrieveFileResponse {
|
||||||
|
file_data: response.to_vec(),
|
||||||
|
}),
|
||||||
|
..data.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_error_response(
|
||||||
|
&self,
|
||||||
|
res: types::Response,
|
||||||
|
) -> CustomResult<types::ErrorResponse, errors::ConnectorError> {
|
||||||
|
let response: stripe::ErrorResponse = res
|
||||||
|
.response
|
||||||
|
.parse_struct("ErrorResponse")
|
||||||
|
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
|
||||||
|
Ok(types::ErrorResponse {
|
||||||
|
status_code: res.status_code,
|
||||||
|
code: response
|
||||||
|
.error
|
||||||
|
.code
|
||||||
|
.unwrap_or_else(|| consts::NO_ERROR_CODE.to_string()),
|
||||||
|
message: response
|
||||||
|
.error
|
||||||
|
.message
|
||||||
|
.unwrap_or_else(|| consts::NO_ERROR_MESSAGE.to_string()),
|
||||||
|
reason: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl api::SubmitEvidence for Stripe {}
|
impl api::SubmitEvidence for Stripe {}
|
||||||
|
|
||||||
impl
|
impl
|
||||||
|
|||||||
@ -3,7 +3,7 @@ use common_utils::errors::CustomResult;
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{errors, files::helpers::retrieve_file_and_provider_file_id_from_file_id},
|
core::{errors, files::helpers::retrieve_file_and_provider_file_id_from_file_id},
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
types::SubmitEvidenceRequestData,
|
types::{api, SubmitEvidenceRequestData},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn get_evidence_request_data(
|
pub async fn get_evidence_request_data(
|
||||||
@ -17,6 +17,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.cancellation_policy,
|
evidence_request.cancellation_policy,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (customer_communication, customer_communication_provider_file_id) =
|
let (customer_communication, customer_communication_provider_file_id) =
|
||||||
@ -24,6 +25,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.customer_communication,
|
evidence_request.customer_communication,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (customer_signature, customer_signature_provider_file_id) =
|
let (customer_signature, customer_signature_provider_file_id) =
|
||||||
@ -31,12 +33,14 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.customer_signature,
|
evidence_request.customer_signature,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (receipt, receipt_provider_file_id) = retrieve_file_and_provider_file_id_from_file_id(
|
let (receipt, receipt_provider_file_id) = retrieve_file_and_provider_file_id_from_file_id(
|
||||||
state,
|
state,
|
||||||
evidence_request.receipt,
|
evidence_request.receipt,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (refund_policy, refund_policy_provider_file_id) =
|
let (refund_policy, refund_policy_provider_file_id) =
|
||||||
@ -44,6 +48,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.refund_policy,
|
evidence_request.refund_policy,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (service_documentation, service_documentation_provider_file_id) =
|
let (service_documentation, service_documentation_provider_file_id) =
|
||||||
@ -51,6 +56,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.service_documentation,
|
evidence_request.service_documentation,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (shipping_documentation, shipping_documentation_provider_file_id) =
|
let (shipping_documentation, shipping_documentation_provider_file_id) =
|
||||||
@ -58,6 +64,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.shipping_documentation,
|
evidence_request.shipping_documentation,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (
|
let (
|
||||||
@ -67,6 +74,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.invoice_showing_distinct_transactions,
|
evidence_request.invoice_showing_distinct_transactions,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (recurring_transaction_agreement, recurring_transaction_agreement_provider_file_id) =
|
let (recurring_transaction_agreement, recurring_transaction_agreement_provider_file_id) =
|
||||||
@ -74,6 +82,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.recurring_transaction_agreement,
|
evidence_request.recurring_transaction_agreement,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let (uncategorized_file, uncategorized_file_provider_file_id) =
|
let (uncategorized_file, uncategorized_file_provider_file_id) =
|
||||||
@ -81,6 +90,7 @@ pub async fn get_evidence_request_data(
|
|||||||
state,
|
state,
|
||||||
evidence_request.uncategorized_file,
|
evidence_request.uncategorized_file,
|
||||||
merchant_account,
|
merchant_account,
|
||||||
|
api::FileDataRequired::NotRequired,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(SubmitEvidenceRequestData {
|
Ok(SubmitEvidenceRequestData {
|
||||||
|
|||||||
@ -13,7 +13,7 @@ use crate::{
|
|||||||
consts,
|
consts,
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
services::{self, ApplicationResponse},
|
services::{self, ApplicationResponse},
|
||||||
types::{api, storage, transformers::ForeignInto},
|
types::{api, storage},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn files_create_core(
|
pub async fn files_create_core(
|
||||||
@ -37,6 +37,7 @@ pub async fn files_create_core(
|
|||||||
provider_file_id: None,
|
provider_file_id: None,
|
||||||
file_upload_provider: None,
|
file_upload_provider: None,
|
||||||
available: false,
|
available: false,
|
||||||
|
connector_label: None,
|
||||||
};
|
};
|
||||||
let file_metadata_object = state
|
let file_metadata_object = state
|
||||||
.store
|
.store
|
||||||
@ -44,8 +45,8 @@ pub async fn files_create_core(
|
|||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
.attach_printable("Unable to insert file_metadata")?;
|
.attach_printable("Unable to insert file_metadata")?;
|
||||||
let (provider_file_id, file_upload_provider) =
|
let (provider_file_id, file_upload_provider, connector_label) =
|
||||||
helpers::upload_and_get_provider_provider_file_id(
|
helpers::upload_and_get_provider_provider_file_id_connector_label(
|
||||||
state,
|
state,
|
||||||
&merchant_account,
|
&merchant_account,
|
||||||
&create_file_request,
|
&create_file_request,
|
||||||
@ -55,8 +56,9 @@ pub async fn files_create_core(
|
|||||||
//Update file metadata
|
//Update file metadata
|
||||||
let update_file_metadata = storage_models::file::FileMetadataUpdate::Update {
|
let update_file_metadata = storage_models::file::FileMetadataUpdate::Update {
|
||||||
provider_file_id: Some(provider_file_id),
|
provider_file_id: Some(provider_file_id),
|
||||||
file_upload_provider: Some(file_upload_provider.foreign_into()),
|
file_upload_provider: Some(file_upload_provider),
|
||||||
available: true,
|
available: true,
|
||||||
|
connector_label,
|
||||||
};
|
};
|
||||||
state
|
state
|
||||||
.store
|
.store
|
||||||
@ -102,6 +104,7 @@ pub async fn files_retrieve_core(
|
|||||||
state,
|
state,
|
||||||
Some(req.file_id),
|
Some(req.file_id),
|
||||||
&merchant_account,
|
&merchant_account,
|
||||||
|
api::FileDataRequired::Required,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let content_type = file_metadata_object
|
let content_type = file_metadata_object
|
||||||
|
|||||||
@ -6,11 +6,13 @@ use futures::TryStreamExt;
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::{
|
core::{
|
||||||
errors::{self, StorageErrorExt},
|
errors::{self, StorageErrorExt},
|
||||||
files, payments, utils,
|
files,
|
||||||
|
payments::{self, helpers as payments_helpers},
|
||||||
|
utils,
|
||||||
},
|
},
|
||||||
routes::AppState,
|
routes::AppState,
|
||||||
services,
|
services,
|
||||||
types::{self, api, storage},
|
types::{self, api, storage, transformers::ForeignTryFrom},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn read_string(field: &mut Field) -> Option<String> {
|
pub async fn read_string(field: &mut Field) -> Option<String> {
|
||||||
@ -144,10 +146,66 @@ pub async fn delete_file_using_file_id(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn retrieve_file_from_connector(
|
||||||
|
state: &AppState,
|
||||||
|
file_metadata: storage_models::file::FileMetadata,
|
||||||
|
merchant_account: &storage_models::merchant_account::MerchantAccount,
|
||||||
|
) -> CustomResult<Vec<u8>, errors::ApiErrorResponse> {
|
||||||
|
let connector = &types::Connector::foreign_try_from(
|
||||||
|
file_metadata
|
||||||
|
.file_upload_provider
|
||||||
|
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.into_report()
|
||||||
|
.attach_printable("Missing file upload provider")?,
|
||||||
|
)?
|
||||||
|
.to_string();
|
||||||
|
let connector_data = api::ConnectorData::get_connector_by_name(
|
||||||
|
&state.conf.connectors,
|
||||||
|
connector,
|
||||||
|
api::GetToken::Connector,
|
||||||
|
)?;
|
||||||
|
let connector_integration: services::BoxedConnectorIntegration<
|
||||||
|
'_,
|
||||||
|
api::Retrieve,
|
||||||
|
types::RetrieveFileRequestData,
|
||||||
|
types::RetrieveFileResponse,
|
||||||
|
> = connector_data.connector.get_connector_integration();
|
||||||
|
let router_data = utils::construct_retrieve_file_router_data(
|
||||||
|
state,
|
||||||
|
merchant_account,
|
||||||
|
&file_metadata,
|
||||||
|
connector,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed constructing the retrieve file router data")?;
|
||||||
|
let response = services::execute_connector_processing_step(
|
||||||
|
state,
|
||||||
|
connector_integration,
|
||||||
|
&router_data,
|
||||||
|
payments::CallConnectorAction::Trigger,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.attach_printable("Failed while calling retrieve file connector api")?;
|
||||||
|
let retrieve_file_response =
|
||||||
|
response
|
||||||
|
.response
|
||||||
|
.map_err(|err| errors::ApiErrorResponse::ExternalConnectorError {
|
||||||
|
code: err.code,
|
||||||
|
message: err.message,
|
||||||
|
connector: connector.to_string(),
|
||||||
|
status_code: err.status_code,
|
||||||
|
reason: err.reason,
|
||||||
|
})?;
|
||||||
|
Ok(retrieve_file_response.file_data)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn retrieve_file_and_provider_file_id_from_file_id(
|
pub async fn retrieve_file_and_provider_file_id_from_file_id(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
file_id: Option<String>,
|
file_id: Option<String>,
|
||||||
merchant_account: &storage_models::merchant_account::MerchantAccount,
|
merchant_account: &storage_models::merchant_account::MerchantAccount,
|
||||||
|
is_connector_file_data_required: api::FileDataRequired,
|
||||||
) -> CustomResult<(Option<Vec<u8>>, Option<String>), errors::ApiErrorResponse> {
|
) -> CustomResult<(Option<Vec<u8>>, Option<String>), errors::ApiErrorResponse> {
|
||||||
match file_id {
|
match file_id {
|
||||||
None => Ok((None, None)),
|
None => Ok((None, None)),
|
||||||
@ -159,10 +217,13 @@ pub async fn retrieve_file_and_provider_file_id_from_file_id(
|
|||||||
.change_context(errors::ApiErrorResponse::FileNotFound)?;
|
.change_context(errors::ApiErrorResponse::FileNotFound)?;
|
||||||
let (provider, provider_file_id) = match (
|
let (provider, provider_file_id) = match (
|
||||||
file_metadata_object.file_upload_provider,
|
file_metadata_object.file_upload_provider,
|
||||||
file_metadata_object.provider_file_id,
|
file_metadata_object.provider_file_id.clone(),
|
||||||
|
file_metadata_object.available,
|
||||||
) {
|
) {
|
||||||
(Some(provider), Some(provider_file_id)) => (provider, provider_file_id),
|
(Some(provider), Some(provider_file_id), true) => (provider, provider_file_id),
|
||||||
_ => Err(errors::ApiErrorResponse::FileNotFound)?,
|
_ => Err(errors::ApiErrorResponse::FileNotAvailable)
|
||||||
|
.into_report()
|
||||||
|
.attach_printable("File not available")?,
|
||||||
};
|
};
|
||||||
match provider {
|
match provider {
|
||||||
storage_models::enums::FileUploadProvider::Router => Ok((
|
storage_models::enums::FileUploadProvider::Router => Ok((
|
||||||
@ -176,20 +237,39 @@ pub async fn retrieve_file_and_provider_file_id_from_file_id(
|
|||||||
),
|
),
|
||||||
Some(provider_file_id),
|
Some(provider_file_id),
|
||||||
)),
|
)),
|
||||||
//TODO: Handle Retrieve for other providers
|
_ => {
|
||||||
_ => Ok((None, Some(provider_file_id))),
|
let connector_file_data = match is_connector_file_data_required {
|
||||||
|
api::FileDataRequired::Required => Some(
|
||||||
|
retrieve_file_from_connector(
|
||||||
|
state,
|
||||||
|
file_metadata_object,
|
||||||
|
merchant_account,
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
),
|
||||||
|
api::FileDataRequired::NotRequired => None,
|
||||||
|
};
|
||||||
|
Ok((connector_file_data, Some(provider_file_id)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Upload file to connector if it supports / store it in S3 and return file_upload_provider, provider_file_id accordingly
|
//Upload file to connector if it supports / store it in S3 and return file_upload_provider, provider_file_id accordingly
|
||||||
pub async fn upload_and_get_provider_provider_file_id(
|
pub async fn upload_and_get_provider_provider_file_id_connector_label(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
merchant_account: &storage::merchant_account::MerchantAccount,
|
merchant_account: &storage::merchant_account::MerchantAccount,
|
||||||
create_file_request: &api::CreateFileRequest,
|
create_file_request: &api::CreateFileRequest,
|
||||||
file_key: String,
|
file_key: String,
|
||||||
) -> CustomResult<(String, api::FileUploadProvider), errors::ApiErrorResponse> {
|
) -> CustomResult<
|
||||||
|
(
|
||||||
|
String,
|
||||||
|
api_models::enums::FileUploadProvider,
|
||||||
|
Option<String>,
|
||||||
|
),
|
||||||
|
errors::ApiErrorResponse,
|
||||||
|
> {
|
||||||
match create_file_request.purpose {
|
match create_file_request.purpose {
|
||||||
api::FilePurpose::DisputeEvidence => {
|
api::FilePurpose::DisputeEvidence => {
|
||||||
let dispute_id = create_file_request
|
let dispute_id = create_file_request
|
||||||
@ -225,6 +305,12 @@ pub async fn upload_and_get_provider_provider_file_id(
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::PaymentNotFound)?;
|
.change_context(errors::ApiErrorResponse::PaymentNotFound)?;
|
||||||
|
let connector_label = payments_helpers::get_connector_label(
|
||||||
|
payment_intent.business_country,
|
||||||
|
&payment_intent.business_label,
|
||||||
|
payment_attempt.business_sub_label.as_ref(),
|
||||||
|
&dispute.connector,
|
||||||
|
);
|
||||||
let connector_integration: services::BoxedConnectorIntegration<
|
let connector_integration: services::BoxedConnectorIntegration<
|
||||||
'_,
|
'_,
|
||||||
api::Upload,
|
api::Upload,
|
||||||
@ -239,6 +325,7 @@ pub async fn upload_and_get_provider_provider_file_id(
|
|||||||
create_file_request,
|
create_file_request,
|
||||||
&dispute.connector,
|
&dispute.connector,
|
||||||
file_key,
|
file_key,
|
||||||
|
connector_label.clone(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
@ -263,7 +350,10 @@ pub async fn upload_and_get_provider_provider_file_id(
|
|||||||
})?;
|
})?;
|
||||||
Ok((
|
Ok((
|
||||||
upload_file_response.provider_file_id,
|
upload_file_response.provider_file_id,
|
||||||
api::FileUploadProvider::try_from(&connector_data.connector_name)?,
|
api_models::enums::FileUploadProvider::foreign_try_from(
|
||||||
|
&connector_data.connector_name,
|
||||||
|
)?,
|
||||||
|
Some(connector_label),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
upload_file(
|
upload_file(
|
||||||
@ -273,7 +363,11 @@ pub async fn upload_and_get_provider_provider_file_id(
|
|||||||
create_file_request.file.clone(),
|
create_file_request.file.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok((file_key, api::FileUploadProvider::Router))
|
Ok((
|
||||||
|
file_key,
|
||||||
|
api_models::enums::FileUploadProvider::Router,
|
||||||
|
None,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -324,6 +324,14 @@ macro_rules! default_imp_for_file_upload{
|
|||||||
types::UploadFileResponse,
|
types::UploadFileResponse,
|
||||||
> for $path::$connector
|
> for $path::$connector
|
||||||
{}
|
{}
|
||||||
|
impl api::RetrieveFile for $path::$connector {}
|
||||||
|
impl
|
||||||
|
services::ConnectorIntegration<
|
||||||
|
api::Retrieve,
|
||||||
|
types::RetrieveFileRequestData,
|
||||||
|
types::RetrieveFileResponse,
|
||||||
|
> for $path::$connector
|
||||||
|
{}
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use std::marker::PhantomData;
|
|||||||
|
|
||||||
use api_models::enums::{DisputeStage, DisputeStatus};
|
use api_models::enums::{DisputeStage, DisputeStatus};
|
||||||
use common_utils::errors::CustomResult;
|
use common_utils::errors::CustomResult;
|
||||||
use error_stack::ResultExt;
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use router_env::{instrument, tracing};
|
use router_env::{instrument, tracing};
|
||||||
|
|
||||||
use super::payments::{helpers, PaymentAddress};
|
use super::payments::{helpers, PaymentAddress};
|
||||||
@ -343,6 +343,7 @@ pub async fn construct_submit_evidence_router_data<'a>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn construct_upload_file_router_data<'a>(
|
pub async fn construct_upload_file_router_data<'a>(
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
payment_intent: &'a storage::PaymentIntent,
|
payment_intent: &'a storage::PaymentIntent,
|
||||||
@ -351,13 +352,8 @@ pub async fn construct_upload_file_router_data<'a>(
|
|||||||
create_file_request: &types::api::CreateFileRequest,
|
create_file_request: &types::api::CreateFileRequest,
|
||||||
connector_id: &str,
|
connector_id: &str,
|
||||||
file_key: String,
|
file_key: String,
|
||||||
|
connector_label: String,
|
||||||
) -> RouterResult<types::UploadFileRouterData> {
|
) -> RouterResult<types::UploadFileRouterData> {
|
||||||
let connector_label = helpers::get_connector_label(
|
|
||||||
payment_intent.business_country,
|
|
||||||
&payment_intent.business_label,
|
|
||||||
payment_attempt.business_sub_label.as_ref(),
|
|
||||||
connector_id,
|
|
||||||
);
|
|
||||||
let merchant_connector_account = helpers::get_merchant_connector_account(
|
let merchant_connector_account = helpers::get_merchant_connector_account(
|
||||||
state,
|
state,
|
||||||
merchant_account.merchant_id.as_str(),
|
merchant_account.merchant_id.as_str(),
|
||||||
@ -465,3 +461,62 @@ pub async fn construct_defend_dispute_router_data<'a>(
|
|||||||
};
|
};
|
||||||
Ok(router_data)
|
Ok(router_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip_all)]
|
||||||
|
pub async fn construct_retrieve_file_router_data<'a>(
|
||||||
|
state: &'a AppState,
|
||||||
|
merchant_account: &storage::MerchantAccount,
|
||||||
|
file_metadata: &storage_models::file::FileMetadata,
|
||||||
|
connector_id: &str,
|
||||||
|
) -> RouterResult<types::RetrieveFileRouterData> {
|
||||||
|
let connector_label = file_metadata
|
||||||
|
.connector_label
|
||||||
|
.clone()
|
||||||
|
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.into_report()
|
||||||
|
.attach_printable("Missing connector label")?;
|
||||||
|
let merchant_connector_account = helpers::get_merchant_connector_account(
|
||||||
|
state,
|
||||||
|
merchant_account.merchant_id.as_str(),
|
||||||
|
&connector_label,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let auth_type: types::ConnectorAuthType = merchant_connector_account
|
||||||
|
.get_connector_account_details()
|
||||||
|
.parse_value("ConnectorAuthType")
|
||||||
|
.change_context(errors::ApiErrorResponse::InternalServerError)?;
|
||||||
|
let router_data = types::RouterData {
|
||||||
|
flow: PhantomData,
|
||||||
|
merchant_id: merchant_account.merchant_id.clone(),
|
||||||
|
connector: connector_id.to_string(),
|
||||||
|
customer_id: None,
|
||||||
|
connector_customer: None,
|
||||||
|
payment_id: "irrelevant_payment_id_in_dispute_flow".to_string(),
|
||||||
|
attempt_id: "irrelevant_attempt_id_in_dispute_flow".to_string(),
|
||||||
|
status: storage_models::enums::AttemptStatus::default(),
|
||||||
|
payment_method: storage_models::enums::PaymentMethod::default(),
|
||||||
|
connector_auth_type: auth_type,
|
||||||
|
description: None,
|
||||||
|
return_url: None,
|
||||||
|
payment_method_id: None,
|
||||||
|
address: PaymentAddress::default(),
|
||||||
|
auth_type: storage_models::enums::AuthenticationType::default(),
|
||||||
|
connector_meta_data: merchant_connector_account.get_metadata(),
|
||||||
|
amount_captured: None,
|
||||||
|
request: types::RetrieveFileRequestData {
|
||||||
|
provider_file_id: file_metadata
|
||||||
|
.provider_file_id
|
||||||
|
.clone()
|
||||||
|
.ok_or(errors::ApiErrorResponse::InternalServerError)
|
||||||
|
.into_report()
|
||||||
|
.attach_printable("Missing provider file id")?,
|
||||||
|
},
|
||||||
|
response: Err(types::ErrorResponse::default()),
|
||||||
|
access_token: None,
|
||||||
|
session_token: None,
|
||||||
|
reference_id: None,
|
||||||
|
payment_method_token: None,
|
||||||
|
};
|
||||||
|
Ok(router_data)
|
||||||
|
}
|
||||||
|
|||||||
@ -136,6 +136,12 @@ pub type SubmitEvidenceType = dyn services::ConnectorIntegration<
|
|||||||
pub type UploadFileType =
|
pub type UploadFileType =
|
||||||
dyn services::ConnectorIntegration<api::Upload, UploadFileRequestData, UploadFileResponse>;
|
dyn services::ConnectorIntegration<api::Upload, UploadFileRequestData, UploadFileResponse>;
|
||||||
|
|
||||||
|
pub type RetrieveFileType = dyn services::ConnectorIntegration<
|
||||||
|
api::Retrieve,
|
||||||
|
RetrieveFileRequestData,
|
||||||
|
RetrieveFileResponse,
|
||||||
|
>;
|
||||||
|
|
||||||
pub type DefendDisputeType = dyn services::ConnectorIntegration<
|
pub type DefendDisputeType = dyn services::ConnectorIntegration<
|
||||||
api::Defend,
|
api::Defend,
|
||||||
DefendDisputeRequestData,
|
DefendDisputeRequestData,
|
||||||
@ -152,6 +158,9 @@ pub type SubmitEvidenceRouterData =
|
|||||||
|
|
||||||
pub type UploadFileRouterData = RouterData<api::Upload, UploadFileRequestData, UploadFileResponse>;
|
pub type UploadFileRouterData = RouterData<api::Upload, UploadFileRequestData, UploadFileResponse>;
|
||||||
|
|
||||||
|
pub type RetrieveFileRouterData =
|
||||||
|
RouterData<api::Retrieve, RetrieveFileRequestData, RetrieveFileResponse>;
|
||||||
|
|
||||||
pub type DefendDisputeRouterData =
|
pub type DefendDisputeRouterData =
|
||||||
RouterData<api::Defend, DefendDisputeRequestData, DefendDisputeResponse>;
|
RouterData<api::Defend, DefendDisputeRequestData, DefendDisputeResponse>;
|
||||||
|
|
||||||
@ -516,6 +525,16 @@ pub struct UploadFileResponse {
|
|||||||
pub provider_file_id: String,
|
pub provider_file_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RetrieveFileRequestData {
|
||||||
|
pub provider_file_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct RetrieveFileResponse {
|
||||||
|
pub file_data: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct ConnectorResponse {
|
pub struct ConnectorResponse {
|
||||||
pub merchant_id: String,
|
pub merchant_id: String,
|
||||||
|
|||||||
@ -1,23 +1,41 @@
|
|||||||
|
use api_models::enums::FileUploadProvider;
|
||||||
use masking::{Deserialize, Serialize};
|
use masking::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::ConnectorCommon;
|
use super::ConnectorCommon;
|
||||||
use crate::{core::errors, services, types};
|
use crate::{
|
||||||
|
core::errors,
|
||||||
|
services,
|
||||||
|
types::{self, transformers::ForeignTryFrom},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug, Deserialize, Serialize)]
|
#[derive(Default, Debug, Deserialize, Serialize)]
|
||||||
pub struct FileId {
|
pub struct FileId {
|
||||||
pub file_id: String,
|
pub file_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, frunk::LabelledGeneric)]
|
#[derive(Debug)]
|
||||||
pub enum FileUploadProvider {
|
pub enum FileDataRequired {
|
||||||
Router,
|
Required,
|
||||||
Stripe,
|
NotRequired,
|
||||||
Checkout,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<&types::Connector> for FileUploadProvider {
|
impl ForeignTryFrom<FileUploadProvider> for types::Connector {
|
||||||
type Error = error_stack::Report<errors::ApiErrorResponse>;
|
type Error = error_stack::Report<errors::ApiErrorResponse>;
|
||||||
fn try_from(item: &types::Connector) -> Result<Self, Self::Error> {
|
fn foreign_try_from(item: FileUploadProvider) -> Result<Self, Self::Error> {
|
||||||
|
match item {
|
||||||
|
FileUploadProvider::Stripe => Ok(Self::Stripe),
|
||||||
|
FileUploadProvider::Checkout => Ok(Self::Checkout),
|
||||||
|
FileUploadProvider::Router => Err(errors::ApiErrorResponse::NotSupported {
|
||||||
|
message: "File upload provider is not a connector".to_owned(),
|
||||||
|
}
|
||||||
|
.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ForeignTryFrom<&types::Connector> for FileUploadProvider {
|
||||||
|
type Error = error_stack::Report<errors::ApiErrorResponse>;
|
||||||
|
fn foreign_try_from(item: &types::Connector) -> Result<Self, Self::Error> {
|
||||||
match *item {
|
match *item {
|
||||||
types::Connector::Stripe => Ok(Self::Stripe),
|
types::Connector::Stripe => Ok(Self::Stripe),
|
||||||
types::Connector::Checkout => Ok(Self::Checkout),
|
types::Connector::Checkout => Ok(Self::Checkout),
|
||||||
@ -54,7 +72,19 @@ pub trait UploadFile:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait FileUpload: ConnectorCommon + Sync + UploadFile {
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Retrieve;
|
||||||
|
|
||||||
|
pub trait RetrieveFile:
|
||||||
|
services::ConnectorIntegration<
|
||||||
|
Retrieve,
|
||||||
|
types::RetrieveFileRequestData,
|
||||||
|
types::RetrieveFileResponse,
|
||||||
|
>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait FileUpload: ConnectorCommon + Sync + UploadFile + RetrieveFile {
|
||||||
fn validate_file_upload(
|
fn validate_file_upload(
|
||||||
&self,
|
&self,
|
||||||
_purpose: FilePurpose,
|
_purpose: FilePurpose,
|
||||||
|
|||||||
@ -457,12 +457,6 @@ impl ForeignFrom<storage_enums::DisputeStatus> for api_enums::DisputeStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ForeignFrom<api_types::FileUploadProvider> for storage_enums::FileUploadProvider {
|
|
||||||
fn foreign_from(provider: api_types::FileUploadProvider) -> Self {
|
|
||||||
frunk::labelled_convert_from(provider)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ForeignTryFrom<api_models::webhooks::IncomingWebhookEvent> for storage_enums::DisputeStatus {
|
impl ForeignTryFrom<api_models::webhooks::IncomingWebhookEvent> for storage_enums::DisputeStatus {
|
||||||
type Error = errors::ValidationError;
|
type Error = errors::ValidationError;
|
||||||
|
|
||||||
|
|||||||
@ -796,26 +796,3 @@ pub enum DisputeStatus {
|
|||||||
DisputeWon,
|
DisputeWon,
|
||||||
DisputeLost,
|
DisputeLost,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
|
||||||
Clone,
|
|
||||||
Copy,
|
|
||||||
Debug,
|
|
||||||
Eq,
|
|
||||||
PartialEq,
|
|
||||||
Default,
|
|
||||||
serde::Deserialize,
|
|
||||||
serde::Serialize,
|
|
||||||
strum::Display,
|
|
||||||
strum::EnumString,
|
|
||||||
frunk::LabelledGeneric,
|
|
||||||
)]
|
|
||||||
#[router_derive::diesel_enum(storage_type = "text")]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
#[strum(serialize_all = "snake_case")]
|
|
||||||
pub enum FileUploadProvider {
|
|
||||||
#[default]
|
|
||||||
Router,
|
|
||||||
Stripe,
|
|
||||||
Checkout,
|
|
||||||
}
|
|
||||||
|
|||||||
@ -2,7 +2,7 @@ use common_utils::custom_serde;
|
|||||||
use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
|
use diesel::{AsChangeset, Identifiable, Insertable, Queryable};
|
||||||
use masking::{Deserialize, Serialize};
|
use masking::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{enums as storage_enums, schema::file_metadata};
|
use crate::schema::file_metadata;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Insertable, Serialize, router_derive::DebugAsDisplay)]
|
#[derive(Clone, Debug, Deserialize, Insertable, Serialize, router_derive::DebugAsDisplay)]
|
||||||
#[diesel(table_name = file_metadata)]
|
#[diesel(table_name = file_metadata)]
|
||||||
@ -14,8 +14,9 @@ pub struct FileMetadataNew {
|
|||||||
pub file_size: i32,
|
pub file_size: i32,
|
||||||
pub file_type: String,
|
pub file_type: String,
|
||||||
pub provider_file_id: Option<String>,
|
pub provider_file_id: Option<String>,
|
||||||
pub file_upload_provider: Option<storage_enums::FileUploadProvider>,
|
pub file_upload_provider: Option<common_enums::FileUploadProvider>,
|
||||||
pub available: bool,
|
pub available: bool,
|
||||||
|
pub connector_label: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable)]
|
#[derive(Clone, Debug, Deserialize, Serialize, Identifiable, Queryable)]
|
||||||
@ -28,18 +29,20 @@ pub struct FileMetadata {
|
|||||||
pub file_size: i32,
|
pub file_size: i32,
|
||||||
pub file_type: String,
|
pub file_type: String,
|
||||||
pub provider_file_id: Option<String>,
|
pub provider_file_id: Option<String>,
|
||||||
pub file_upload_provider: Option<storage_enums::FileUploadProvider>,
|
pub file_upload_provider: Option<common_enums::FileUploadProvider>,
|
||||||
pub available: bool,
|
pub available: bool,
|
||||||
#[serde(with = "custom_serde::iso8601")]
|
#[serde(with = "custom_serde::iso8601")]
|
||||||
pub created_at: time::PrimitiveDateTime,
|
pub created_at: time::PrimitiveDateTime,
|
||||||
|
pub connector_label: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum FileMetadataUpdate {
|
pub enum FileMetadataUpdate {
|
||||||
Update {
|
Update {
|
||||||
provider_file_id: Option<String>,
|
provider_file_id: Option<String>,
|
||||||
file_upload_provider: Option<storage_enums::FileUploadProvider>,
|
file_upload_provider: Option<common_enums::FileUploadProvider>,
|
||||||
available: bool,
|
available: bool,
|
||||||
|
connector_label: Option<String>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +50,9 @@ pub enum FileMetadataUpdate {
|
|||||||
#[diesel(table_name = file_metadata)]
|
#[diesel(table_name = file_metadata)]
|
||||||
pub struct FileMetadataUpdateInternal {
|
pub struct FileMetadataUpdateInternal {
|
||||||
provider_file_id: Option<String>,
|
provider_file_id: Option<String>,
|
||||||
file_upload_provider: Option<storage_enums::FileUploadProvider>,
|
file_upload_provider: Option<common_enums::FileUploadProvider>,
|
||||||
available: bool,
|
available: bool,
|
||||||
|
connector_label: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<FileMetadataUpdate> for FileMetadataUpdateInternal {
|
impl From<FileMetadataUpdate> for FileMetadataUpdateInternal {
|
||||||
@ -58,10 +62,12 @@ impl From<FileMetadataUpdate> for FileMetadataUpdateInternal {
|
|||||||
provider_file_id,
|
provider_file_id,
|
||||||
file_upload_provider,
|
file_upload_provider,
|
||||||
available,
|
available,
|
||||||
|
connector_label,
|
||||||
} => Self {
|
} => Self {
|
||||||
provider_file_id,
|
provider_file_id,
|
||||||
file_upload_provider,
|
file_upload_provider,
|
||||||
available,
|
available,
|
||||||
|
connector_label,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -169,6 +169,7 @@ diesel::table! {
|
|||||||
file_upload_provider -> Nullable<Varchar>,
|
file_upload_provider -> Nullable<Varchar>,
|
||||||
available -> Bool,
|
available -> Bool,
|
||||||
created_at -> Timestamp,
|
created_at -> Timestamp,
|
||||||
|
connector_label -> Nullable<Varchar>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
ALTER TABLE file_metadata DROP COLUMN connector_label;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
-- Your SQL goes here
|
||||||
|
ALTER TABLE file_metadata
|
||||||
|
ADD COLUMN connector_label VARCHAR(255);
|
||||||
Reference in New Issue
Block a user