mirror of
				https://github.com/juspay/hyperswitch.git
				synced 2025-10-31 10:06:32 +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 customers; | ||||||
| mod payment_intents; | mod payment_intents; | ||||||
| mod refunds; | mod refunds; | ||||||
|  | mod setup_intents; | ||||||
| use actix_web::{web, Scope}; | use actix_web::{web, Scope}; | ||||||
| mod errors; | mod errors; | ||||||
| pub(crate) use errors::ErrorCode; | 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; | use crate::routes::AppState; | ||||||
| pub struct StripeApis; | pub struct StripeApis; | ||||||
|  |  | ||||||
| @ -16,6 +17,7 @@ impl StripeApis { | |||||||
|         let strict = false; |         let strict = false; | ||||||
|         web::scope("/vs/v1") |         web::scope("/vs/v1") | ||||||
|             .app_data(web::Data::new(serde_qs::Config::new(max_depth, strict))) |             .app_data(web::Data::new(serde_qs::Config::new(max_depth, strict))) | ||||||
|  |             .service(SetupIntents::server(state.clone())) | ||||||
|             .service(PaymentIntents::server(state.clone())) |             .service(PaymentIntents::server(state.clone())) | ||||||
|             .service(Refunds::server(state.clone())) |             .service(Refunds::server(state.clone())) | ||||||
|             .service(Customers::server(state)) |             .service(Customers::server(state)) | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| use actix_web::{web, Scope}; | use actix_web::{web, Scope}; | ||||||
|  |  | ||||||
| use super::{customers::*, payment_intents::*, refunds::*}; | use super::{customers::*, payment_intents::*, refunds::*, setup_intents::*}; | ||||||
| use crate::routes::AppState; | use crate::routes::AppState; | ||||||
|  |  | ||||||
| pub struct PaymentIntents; | 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; | pub struct Refunds; | ||||||
|  |  | ||||||
| impl 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