mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 18:17:13 +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
	 Nishant Joshi
					Nishant Joshi