mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 00:49:42 +08:00
feat(setup_intent): add setup_intent stripe compatibility (#102)
This commit is contained in:
@ -2,11 +2,12 @@ mod app;
|
||||
mod customers;
|
||||
mod payment_intents;
|
||||
mod refunds;
|
||||
mod setup_intents;
|
||||
use actix_web::{web, Scope};
|
||||
mod errors;
|
||||
pub(crate) use errors::ErrorCode;
|
||||
|
||||
pub(crate) use self::app::{Customers, PaymentIntents, Refunds};
|
||||
pub(crate) use self::app::{Customers, PaymentIntents, Refunds, SetupIntents};
|
||||
use crate::routes::AppState;
|
||||
pub struct StripeApis;
|
||||
|
||||
@ -16,6 +17,7 @@ impl StripeApis {
|
||||
let strict = false;
|
||||
web::scope("/vs/v1")
|
||||
.app_data(web::Data::new(serde_qs::Config::new(max_depth, strict)))
|
||||
.service(SetupIntents::server(state.clone()))
|
||||
.service(PaymentIntents::server(state.clone()))
|
||||
.service(Refunds::server(state.clone()))
|
||||
.service(Customers::server(state))
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use actix_web::{web, Scope};
|
||||
|
||||
use super::{customers::*, payment_intents::*, refunds::*};
|
||||
use super::{customers::*, payment_intents::*, refunds::*, setup_intents::*};
|
||||
use crate::routes::AppState;
|
||||
|
||||
pub struct PaymentIntents;
|
||||
@ -17,6 +17,19 @@ impl PaymentIntents {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SetupIntents;
|
||||
|
||||
impl SetupIntents {
|
||||
pub fn server(state: AppState) -> Scope {
|
||||
web::scope("/setup_intents")
|
||||
.app_data(web::Data::new(state))
|
||||
.service(setup_intents_create)
|
||||
.service(setup_intents_retrieve)
|
||||
.service(setup_intents_update)
|
||||
.service(setup_intents_confirm)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Refunds;
|
||||
|
||||
impl Refunds {
|
||||
|
||||
215
crates/router/src/compatibility/stripe/setup_intents.rs
Normal file
215
crates/router/src/compatibility/stripe/setup_intents.rs
Normal file
@ -0,0 +1,215 @@
|
||||
mod types;
|
||||
|
||||
use actix_web::{get, post, web, HttpRequest, HttpResponse};
|
||||
use error_stack::report;
|
||||
use router_env::{tracing, tracing::instrument};
|
||||
|
||||
use crate::{
|
||||
compatibility::{stripe, wrap},
|
||||
core::payments,
|
||||
routes::AppState,
|
||||
services::api,
|
||||
types::api::{self as api_types, PSync, PaymentsRequest, PaymentsRetrieveRequest, Verify},
|
||||
};
|
||||
|
||||
#[post("")]
|
||||
#[instrument(skip_all)]
|
||||
pub async fn setup_intents_create(
|
||||
state: web::Data<AppState>,
|
||||
qs_config: web::Data<serde_qs::Config>,
|
||||
req: HttpRequest,
|
||||
form_payload: web::Bytes,
|
||||
) -> HttpResponse {
|
||||
let payload: types::StripeSetupIntentRequest = match qs_config.deserialize_bytes(&form_payload)
|
||||
{
|
||||
Ok(p) => p,
|
||||
Err(err) => {
|
||||
return api::log_and_return_error_response(report!(stripe::ErrorCode::from(err)))
|
||||
}
|
||||
};
|
||||
|
||||
let create_payment_req: PaymentsRequest = payload.into();
|
||||
|
||||
wrap::compatibility_api_wrap::<
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
types::StripeSetupIntentResponse,
|
||||
stripe::ErrorCode,
|
||||
>(
|
||||
&state,
|
||||
&req,
|
||||
create_payment_req,
|
||||
|state, merchant_account, req| {
|
||||
payments::payments_core::<Verify, api_types::PaymentsResponse, _, _, _>(
|
||||
state,
|
||||
merchant_account,
|
||||
payments::PaymentCreate,
|
||||
req,
|
||||
api::AuthFlow::Merchant,
|
||||
payments::CallConnectorAction::Trigger,
|
||||
)
|
||||
},
|
||||
api::MerchantAuthentication::ApiKey,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
#[get("/{setup_id}")]
|
||||
pub async fn setup_intents_retrieve(
|
||||
state: web::Data<AppState>,
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
) -> HttpResponse {
|
||||
let payload = PaymentsRetrieveRequest {
|
||||
resource_id: api_types::PaymentIdType::PaymentIntentId(path.to_string()),
|
||||
merchant_id: None,
|
||||
force_sync: true,
|
||||
connector: None,
|
||||
param: None,
|
||||
};
|
||||
|
||||
let auth_type = match api::get_auth_type(&req) {
|
||||
Ok(auth_type) => auth_type,
|
||||
Err(err) => return api::log_and_return_error_response(report!(err)),
|
||||
};
|
||||
let auth_flow = api::get_auth_flow(&auth_type);
|
||||
|
||||
wrap::compatibility_api_wrap::<
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
types::StripeSetupIntentResponse,
|
||||
stripe::ErrorCode,
|
||||
>(
|
||||
&state,
|
||||
&req,
|
||||
payload,
|
||||
|state, merchant_account, payload| {
|
||||
payments::payments_core::<PSync, api_types::PaymentsResponse, _, _, _>(
|
||||
state,
|
||||
merchant_account,
|
||||
payments::PaymentStatus,
|
||||
payload,
|
||||
auth_flow,
|
||||
payments::CallConnectorAction::Trigger,
|
||||
)
|
||||
},
|
||||
auth_type,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
#[post("/{setup_id}")]
|
||||
pub async fn setup_intents_update(
|
||||
state: web::Data<AppState>,
|
||||
qs_config: web::Data<serde_qs::Config>,
|
||||
req: HttpRequest,
|
||||
form_payload: web::Bytes,
|
||||
path: web::Path<String>,
|
||||
) -> HttpResponse {
|
||||
let setup_id = path.into_inner();
|
||||
let stripe_payload: types::StripeSetupIntentRequest =
|
||||
match qs_config.deserialize_bytes(&form_payload) {
|
||||
Ok(p) => p,
|
||||
Err(err) => {
|
||||
return api::log_and_return_error_response(report!(stripe::ErrorCode::from(err)))
|
||||
}
|
||||
};
|
||||
|
||||
let mut payload: PaymentsRequest = stripe_payload.into();
|
||||
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id));
|
||||
|
||||
let auth_type;
|
||||
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
|
||||
Ok(values) => values,
|
||||
Err(err) => return api::log_and_return_error_response(err),
|
||||
};
|
||||
let auth_flow = api::get_auth_flow(&auth_type);
|
||||
wrap::compatibility_api_wrap::<
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
types::StripeSetupIntentResponse,
|
||||
stripe::ErrorCode,
|
||||
>(
|
||||
&state,
|
||||
&req,
|
||||
payload,
|
||||
|state, merchant_account, req| {
|
||||
payments::payments_core::<Verify, api_types::PaymentsResponse, _, _, _>(
|
||||
state,
|
||||
merchant_account,
|
||||
payments::PaymentUpdate,
|
||||
req,
|
||||
auth_flow,
|
||||
payments::CallConnectorAction::Trigger,
|
||||
)
|
||||
},
|
||||
auth_type,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[instrument(skip_all)]
|
||||
#[post("/{setup_id}/confirm")]
|
||||
pub async fn setup_intents_confirm(
|
||||
state: web::Data<AppState>,
|
||||
qs_config: web::Data<serde_qs::Config>,
|
||||
req: HttpRequest,
|
||||
form_payload: web::Bytes,
|
||||
path: web::Path<String>,
|
||||
) -> HttpResponse {
|
||||
let setup_id = path.into_inner();
|
||||
let stripe_payload: types::StripeSetupIntentRequest =
|
||||
match qs_config.deserialize_bytes(&form_payload) {
|
||||
Ok(p) => p,
|
||||
Err(err) => {
|
||||
return api::log_and_return_error_response(report!(stripe::ErrorCode::from(err)))
|
||||
}
|
||||
};
|
||||
|
||||
let mut payload: PaymentsRequest = stripe_payload.into();
|
||||
payload.payment_id = Some(api_types::PaymentIdType::PaymentIntentId(setup_id));
|
||||
payload.confirm = Some(true);
|
||||
|
||||
let auth_type;
|
||||
(payload, auth_type) = match api::get_auth_type_and_check_client_secret(&req, payload) {
|
||||
Ok(values) => values,
|
||||
Err(err) => return api::log_and_return_error_response(err),
|
||||
};
|
||||
let auth_flow = api::get_auth_flow(&auth_type);
|
||||
wrap::compatibility_api_wrap::<
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
types::StripeSetupIntentResponse,
|
||||
stripe::ErrorCode,
|
||||
>(
|
||||
&state,
|
||||
&req,
|
||||
payload,
|
||||
|state, merchant_account, req| {
|
||||
payments::payments_core::<Verify, api_types::PaymentsResponse, _, _, _>(
|
||||
state,
|
||||
merchant_account,
|
||||
payments::PaymentConfirm,
|
||||
req,
|
||||
auth_flow,
|
||||
payments::CallConnectorAction::Trigger,
|
||||
)
|
||||
},
|
||||
auth_type,
|
||||
)
|
||||
.await
|
||||
}
|
||||
326
crates/router/src/compatibility/stripe/setup_intents/types.rs
Normal file
326
crates/router/src/compatibility/stripe/setup_intents/types.rs
Normal file
@ -0,0 +1,326 @@
|
||||
use router_env::logger;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{
|
||||
core::errors,
|
||||
pii::Secret,
|
||||
types::api::{
|
||||
self as api_types, enums as api_enums, Address, AddressDetails, CCard,
|
||||
PaymentListConstraints, PaymentMethod, PaymentsCancelRequest, PaymentsRequest,
|
||||
PaymentsResponse, PhoneDetails, RefundResponse,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
|
||||
pub(crate) struct StripeBillingDetails {
|
||||
pub(crate) address: Option<AddressDetails>,
|
||||
pub(crate) email: Option<String>,
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) phone: Option<String>,
|
||||
}
|
||||
|
||||
impl From<StripeBillingDetails> for Address {
|
||||
fn from(details: StripeBillingDetails) -> Self {
|
||||
Self {
|
||||
address: details.address,
|
||||
phone: Some(PhoneDetails {
|
||||
number: details.phone.map(Secret::new),
|
||||
country_code: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
|
||||
pub(crate) struct StripeCard {
|
||||
pub(crate) number: String,
|
||||
pub(crate) exp_month: String,
|
||||
pub(crate) exp_year: String,
|
||||
pub(crate) cvc: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum StripePaymentMethodType {
|
||||
#[default]
|
||||
Card,
|
||||
}
|
||||
|
||||
impl From<StripePaymentMethodType> for api_enums::PaymentMethodType {
|
||||
fn from(item: StripePaymentMethodType) -> Self {
|
||||
match item {
|
||||
StripePaymentMethodType::Card => api_enums::PaymentMethodType::Card,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Default, PartialEq, Eq, Deserialize, Clone)]
|
||||
pub(crate) struct StripePaymentMethodData {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) stype: StripePaymentMethodType,
|
||||
pub(crate) billing_details: Option<StripeBillingDetails>,
|
||||
#[serde(flatten)]
|
||||
pub(crate) payment_method_details: Option<StripePaymentMethodDetails>, // enum
|
||||
pub(crate) metadata: Option<Value>,
|
||||
}
|
||||
|
||||
#[derive(Default, PartialEq, Eq, Deserialize, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum StripePaymentMethodDetails {
|
||||
Card(StripeCard),
|
||||
#[default]
|
||||
BankTransfer,
|
||||
}
|
||||
|
||||
impl From<StripeCard> for CCard {
|
||||
fn from(card: StripeCard) -> Self {
|
||||
Self {
|
||||
card_number: Secret::new(card.number),
|
||||
card_exp_month: Secret::new(card.exp_month),
|
||||
card_exp_year: Secret::new(card.exp_year),
|
||||
card_holder_name: Secret::new("stripe_cust".to_owned()),
|
||||
card_cvc: Secret::new(card.cvc),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<StripePaymentMethodDetails> for PaymentMethod {
|
||||
fn from(item: StripePaymentMethodDetails) -> Self {
|
||||
match item {
|
||||
StripePaymentMethodDetails::Card(card) => PaymentMethod::Card(CCard::from(card)),
|
||||
StripePaymentMethodDetails::BankTransfer => PaymentMethod::BankTransfer,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Serialize, PartialEq, Eq, Deserialize, Clone)]
|
||||
pub(crate) struct Shipping {
|
||||
pub(crate) address: Option<AddressDetails>,
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) carrier: Option<String>,
|
||||
pub(crate) phone: Option<String>,
|
||||
pub(crate) tracking_number: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Shipping> for Address {
|
||||
fn from(details: Shipping) -> Self {
|
||||
Self {
|
||||
address: details.address,
|
||||
phone: Some(PhoneDetails {
|
||||
number: details.phone.map(Secret::new),
|
||||
country_code: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Default, PartialEq, Eq, Deserialize, Clone)]
|
||||
pub(crate) struct StripeSetupIntentRequest {
|
||||
pub(crate) confirm: Option<bool>,
|
||||
pub(crate) customer: Option<String>,
|
||||
pub(crate) description: Option<String>,
|
||||
pub(crate) payment_method_data: Option<StripePaymentMethodData>,
|
||||
pub(crate) receipt_email: Option<String>,
|
||||
pub(crate) return_url: Option<String>,
|
||||
pub(crate) setup_future_usage: Option<api_enums::FutureUsage>,
|
||||
pub(crate) shipping: Option<Shipping>,
|
||||
pub(crate) billing_details: Option<StripeBillingDetails>,
|
||||
pub(crate) statement_descriptor: Option<String>,
|
||||
pub(crate) statement_descriptor_suffix: Option<String>,
|
||||
pub(crate) metadata: Option<Value>,
|
||||
pub(crate) client_secret: Option<String>,
|
||||
}
|
||||
|
||||
impl From<StripeSetupIntentRequest> for PaymentsRequest {
|
||||
fn from(item: StripeSetupIntentRequest) -> Self {
|
||||
PaymentsRequest {
|
||||
amount: Some(api_types::Amount::Zero),
|
||||
currency: Some(api_enums::Currency::default().to_string()),
|
||||
capture_method: None,
|
||||
amount_to_capture: None,
|
||||
confirm: item.confirm,
|
||||
customer_id: item.customer,
|
||||
email: item.receipt_email.map(Secret::new),
|
||||
name: item
|
||||
.billing_details
|
||||
.as_ref()
|
||||
.and_then(|b| b.name.as_ref().map(|x| Secret::new(x.to_owned()))),
|
||||
phone: item
|
||||
.shipping
|
||||
.as_ref()
|
||||
.and_then(|s| s.phone.as_ref().map(|x| Secret::new(x.to_owned()))),
|
||||
description: item.description,
|
||||
return_url: item.return_url,
|
||||
payment_method_data: item.payment_method_data.as_ref().and_then(|pmd| {
|
||||
pmd.payment_method_details
|
||||
.as_ref()
|
||||
.map(|spmd| PaymentMethod::from(spmd.to_owned()))
|
||||
}),
|
||||
payment_method: item
|
||||
.payment_method_data
|
||||
.as_ref()
|
||||
.map(|pmd| api_enums::PaymentMethodType::from(pmd.stype.to_owned())),
|
||||
shipping: item.shipping.as_ref().map(|s| Address::from(s.to_owned())),
|
||||
billing: item
|
||||
.billing_details
|
||||
.as_ref()
|
||||
.map(|b| Address::from(b.to_owned())),
|
||||
statement_descriptor_name: item.statement_descriptor,
|
||||
statement_descriptor_suffix: item.statement_descriptor_suffix,
|
||||
metadata: item.metadata,
|
||||
client_secret: item.client_secret,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum StripeSetupStatus {
|
||||
Succeeded,
|
||||
Canceled,
|
||||
#[default]
|
||||
Processing,
|
||||
RequiresAction,
|
||||
RequiresPaymentMethod,
|
||||
RequiresConfirmation,
|
||||
}
|
||||
|
||||
// TODO: Verify if the status are correct
|
||||
impl From<api_enums::IntentStatus> for StripeSetupStatus {
|
||||
fn from(item: api_enums::IntentStatus) -> Self {
|
||||
match item {
|
||||
api_enums::IntentStatus::Succeeded => StripeSetupStatus::Succeeded,
|
||||
api_enums::IntentStatus::Failed => StripeSetupStatus::Canceled, // TODO: should we show canceled or processing
|
||||
api_enums::IntentStatus::Processing => StripeSetupStatus::Processing,
|
||||
api_enums::IntentStatus::RequiresCustomerAction => StripeSetupStatus::RequiresAction,
|
||||
api_enums::IntentStatus::RequiresPaymentMethod => {
|
||||
StripeSetupStatus::RequiresPaymentMethod
|
||||
}
|
||||
api_enums::IntentStatus::RequiresConfirmation => {
|
||||
StripeSetupStatus::RequiresConfirmation
|
||||
}
|
||||
api_enums::IntentStatus::RequiresCapture => {
|
||||
logger::error!("Invalid status change");
|
||||
StripeSetupStatus::Canceled
|
||||
}
|
||||
api_enums::IntentStatus::Cancelled => StripeSetupStatus::Canceled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub(crate) enum CancellationReason {
|
||||
Duplicate,
|
||||
Fraudulent,
|
||||
RequestedByCustomer,
|
||||
Abandoned,
|
||||
}
|
||||
|
||||
impl ToString for CancellationReason {
|
||||
fn to_string(&self) -> String {
|
||||
String::from(match self {
|
||||
Self::Duplicate => "duplicate",
|
||||
Self::Fraudulent => "fradulent",
|
||||
Self::RequestedByCustomer => "requested_by_customer",
|
||||
Self::Abandoned => "abandoned",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Copy, Clone)]
|
||||
pub(crate) struct StripePaymentCancelRequest {
|
||||
cancellation_reason: Option<CancellationReason>,
|
||||
}
|
||||
|
||||
impl From<StripePaymentCancelRequest> for PaymentsCancelRequest {
|
||||
fn from(item: StripePaymentCancelRequest) -> Self {
|
||||
Self {
|
||||
cancellation_reason: item.cancellation_reason.map(|c| c.to_string()),
|
||||
..Self::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Default, Eq, PartialEq, Serialize)]
|
||||
pub(crate) struct StripeSetupIntentResponse {
|
||||
pub(crate) id: Option<String>,
|
||||
pub(crate) object: String,
|
||||
pub(crate) status: StripeSetupStatus,
|
||||
pub(crate) client_secret: Option<Secret<String>>,
|
||||
#[serde(with = "common_utils::custom_serde::iso8601::option")]
|
||||
pub(crate) created: Option<time::PrimitiveDateTime>,
|
||||
pub(crate) customer: Option<String>,
|
||||
pub(crate) refunds: Option<Vec<RefundResponse>>,
|
||||
pub(crate) mandate_id: Option<String>,
|
||||
}
|
||||
|
||||
impl From<PaymentsResponse> for StripeSetupIntentResponse {
|
||||
fn from(resp: PaymentsResponse) -> Self {
|
||||
Self {
|
||||
object: "setup_intent".to_owned(),
|
||||
status: StripeSetupStatus::from(resp.status),
|
||||
client_secret: resp.client_secret,
|
||||
created: resp.created,
|
||||
customer: resp.customer_id,
|
||||
id: resp.payment_id,
|
||||
refunds: resp.refunds,
|
||||
mandate_id: resp.mandate_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, serde::Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct StripePaymentListConstraints {
|
||||
pub customer: Option<String>,
|
||||
pub starting_after: Option<String>,
|
||||
pub ending_before: Option<String>,
|
||||
#[serde(default = "default_limit")]
|
||||
pub limit: i64,
|
||||
pub created: Option<i64>,
|
||||
#[serde(rename = "created[lt]")]
|
||||
pub created_lt: Option<i64>,
|
||||
#[serde(rename = "created[gt]")]
|
||||
pub created_gt: Option<i64>,
|
||||
#[serde(rename = "created[lte]")]
|
||||
pub created_lte: Option<i64>,
|
||||
#[serde(rename = "created[gte]")]
|
||||
pub created_gte: Option<i64>,
|
||||
}
|
||||
|
||||
fn default_limit() -> i64 {
|
||||
10
|
||||
}
|
||||
|
||||
impl TryFrom<StripePaymentListConstraints> for PaymentListConstraints {
|
||||
type Error = error_stack::Report<errors::ApiErrorResponse>;
|
||||
fn try_from(item: StripePaymentListConstraints) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
customer_id: item.customer,
|
||||
starting_after: item.starting_after,
|
||||
ending_before: item.ending_before,
|
||||
limit: item.limit,
|
||||
created: from_timestamp_to_datetime(item.created)?,
|
||||
created_lt: from_timestamp_to_datetime(item.created_lt)?,
|
||||
created_gt: from_timestamp_to_datetime(item.created_gt)?,
|
||||
created_lte: from_timestamp_to_datetime(item.created_lte)?,
|
||||
created_gte: from_timestamp_to_datetime(item.created_gte)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_timestamp_to_datetime(
|
||||
time: Option<i64>,
|
||||
) -> Result<Option<time::PrimitiveDateTime>, errors::ApiErrorResponse> {
|
||||
if let Some(time) = time {
|
||||
let time = time::OffsetDateTime::from_unix_timestamp(time).map_err(|err| {
|
||||
logger::error!("Error: from_unix_timestamp: {}", err);
|
||||
errors::ApiErrorResponse::InvalidRequestData {
|
||||
message: "Error while converting timestamp".to_string(),
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(Some(time::PrimitiveDateTime::new(time.date(), time.time())))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user