feat(subscription): Add endpoint to get Subscription estimate (#9637)

Co-authored-by: hyperswitch-bot[bot] <148525504+hyperswitch-bot[bot]@users.noreply.github.com>
Co-authored-by: Gaurav Rawat <104276743+GauravRawat369@users.noreply.github.com>
This commit is contained in:
Jagan
2025-10-08 18:46:38 +05:30
committed by GitHub
parent 76da7b28bc
commit 15bc0a3b35
13 changed files with 231 additions and 24 deletions

View File

@ -422,3 +422,27 @@ pub async fn get_subscription(
subscription.to_subscription_response(),
))
}
pub async fn get_estimate(
state: SessionState,
merchant_context: MerchantContext,
profile_id: common_utils::id_type::ProfileId,
query: subscription_types::EstimateSubscriptionQuery,
) -> RouterResponse<subscription_types::EstimateSubscriptionResponse> {
let profile =
SubscriptionHandler::find_business_profile(&state, &merchant_context, &profile_id)
.await
.attach_printable("subscriptions: failed to find business profile in get_estimate")?;
let billing_handler = BillingHandler::create(
&state,
merchant_context.get_merchant_account(),
merchant_context.get_merchant_key_store(),
None,
profile,
)
.await?;
let estimate = billing_handler
.get_subscription_estimate(&state, query)
.await?;
Ok(ApplicationResponse::Json(estimate.into()))
}

View File

@ -5,8 +5,8 @@ use common_utils::{ext_traits::ValueExt, pii};
use error_stack::ResultExt;
use hyperswitch_domain_models::{
router_data_v2::flow_common_types::{
GetSubscriptionPlanPricesData, GetSubscriptionPlansData, InvoiceRecordBackData,
SubscriptionCreateData, SubscriptionCustomerData,
GetSubscriptionEstimateData, GetSubscriptionPlanPricesData, GetSubscriptionPlansData,
InvoiceRecordBackData, SubscriptionCreateData, SubscriptionCustomerData,
},
router_request_types::{
revenue_recovery::InvoiceRecordBackRequest, subscriptions as subscription_request_types,
@ -20,7 +20,10 @@ use hyperswitch_domain_models::{
use super::errors;
use crate::{
core::payments as payments_core, routes::SessionState, services, types::api as api_types,
core::{payments as payments_core, subscription::subscription_types},
routes::SessionState,
services,
types::api as api_types,
};
pub struct BillingHandler {
@ -283,6 +286,45 @@ impl BillingHandler {
}
}
pub async fn get_subscription_estimate(
&self,
state: &SessionState,
estimate_request: subscription_types::EstimateSubscriptionQuery,
) -> errors::RouterResult<subscription_response_types::GetSubscriptionEstimateResponse> {
let estimate_req = subscription_request_types::GetSubscriptionEstimateRequest {
price_id: estimate_request.item_price_id.clone(),
};
let router_data = self.build_router_data(
state,
estimate_req,
GetSubscriptionEstimateData {
connector_meta_data: self.connector_metadata.clone(),
},
)?;
let connector_integration = self.connector_data.connector.get_connector_integration();
let response = Box::pin(self.call_connector(
state,
router_data,
"get subscription estimate from connector",
connector_integration,
))
.await?;
match response {
Ok(response_data) => Ok(response_data),
Err(err) => Err(errors::ApiErrorResponse::ExternalConnectorError {
code: err.code,
message: err.message,
connector: self.connector_data.connector_name.to_string(),
status_code: err.status_code,
reason: err.reason,
}
.into()),
}
}
pub async fn get_subscription_plans(
&self,
state: &SessionState,

View File

@ -1187,6 +1187,7 @@ impl Subscription {
subscription::create_subscription(state, req, payload)
}),
))
.service(web::resource("/estimate").route(web::get().to(subscription::get_estimate)))
.service(
web::resource("/plans").route(web::get().to(subscription::get_subscription_plans)),
)

View File

@ -91,6 +91,7 @@ impl From<Flow> for ApiIdentifier {
| Flow::ConfirmSubscription
| Flow::CreateAndConfirmSubscription
| Flow::GetSubscription
| Flow::GetSubscriptionEstimate
| Flow::GetPlansForSubscription => Self::Subscription,
Flow::RetrieveForexFlow => Self::Forex,
Flow::AddToBlocklist => Self::Blocklist,

View File

@ -7,6 +7,7 @@ use std::str::FromStr;
use actix_web::{web, HttpRequest, HttpResponse, Responder};
use api_models::subscription as subscription_types;
use error_stack::report;
use hyperswitch_domain_models::errors;
use router_env::{
tracing::{self, instrument},
@ -252,3 +253,40 @@ pub async fn create_and_confirm_subscription(
))
.await
}
/// add support for get subscription estimate
#[instrument(skip_all)]
pub async fn get_estimate(
state: web::Data<AppState>,
req: HttpRequest,
query: web::Query<subscription_types::EstimateSubscriptionQuery>,
) -> impl Responder {
let flow = Flow::GetSubscriptionEstimate;
let profile_id = match extract_profile_id(&req) {
Ok(id) => id,
Err(response) => return response,
};
let api_auth = auth::ApiKeyAuth {
is_connected_allowed: false,
is_platform_allowed: false,
};
let (auth_type, _auth_flow) = match auth::get_auth_type_and_flow(req.headers(), api_auth) {
Ok(auth) => auth,
Err(err) => return oss_api::log_and_return_error_response(report!(err)),
};
Box::pin(oss_api::server_wrap(
flow,
state,
&req,
query.into_inner(),
|state, auth: auth::AuthenticationData, query, _| {
let merchant_context = domain::MerchantContext::NormalMerchant(Box::new(
domain::Context(auth.merchant_account, auth.key_store),
));
subscription::get_estimate(state, merchant_context, profile_id.clone(), query)
},
&*auth_type,
api_locking::LockAction::NotApplicable,
))
.await
}