Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 99 additions & 9 deletions crates/hyperswitch_connectors/src/connectors/chargebee.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,22 @@ use hyperswitch_domain_models::{
access_token_auth::AccessTokenAuth,
payments::{Authorize, Capture, PSync, PaymentMethodToken, Session, SetupMandate, Void},
refunds::{Execute, RSync},
subscriptions::GetSubscriptionPlans,
subscriptions::{GetSubscriptionEstimate, GetSubscriptionPlans},
},
router_request_types::{
subscriptions::GetSubscriptionPlansRequest, AccessTokenRequestData,
PaymentMethodTokenizationData, PaymentsAuthorizeData, PaymentsCancelData,
PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData, RefundsData,
SetupMandateRequestData,
subscriptions::{GetSubscriptionEstimateRequest, GetSubscriptionPlansRequest},
AccessTokenRequestData, PaymentMethodTokenizationData, PaymentsAuthorizeData,
PaymentsCancelData, PaymentsCaptureData, PaymentsSessionData, PaymentsSyncData,
RefundsData, SetupMandateRequestData,
},
router_response_types::{
subscriptions::GetSubscriptionPlansResponse, ConnectorInfo, PaymentsResponseData,
RefundsResponseData,
subscriptions::{GetSubscriptionEstimateResponse, GetSubscriptionPlansResponse},
ConnectorInfo, PaymentsResponseData, RefundsResponseData,
},
types::{
GetSubscriptionPlansRouterData, PaymentsAuthorizeRouterData, PaymentsCaptureRouterData,
PaymentsSyncRouterData, RefundSyncRouterData, RefundsRouterData,
GetSubscriptionEstimateRouterData, GetSubscriptionPlansRouterData,
PaymentsAuthorizeRouterData, PaymentsCaptureRouterData, PaymentsSyncRouterData,
RefundSyncRouterData, RefundsRouterData,
},
};
use hyperswitch_interfaces::{
Expand Down Expand Up @@ -687,6 +688,95 @@ fn get_chargebee_plans_query_params(
}

impl api::subscriptions::Subscriptions for Chargebee {}

impl api::subscriptions::GetSubscriptionEstimateFlow for Chargebee {}

impl
ConnectorIntegration<
GetSubscriptionEstimate,
GetSubscriptionEstimateRequest,
GetSubscriptionEstimateResponse,
> for Chargebee
{
fn get_headers(
&self,
req: &GetSubscriptionEstimateRouterData,
connectors: &Connectors,
) -> CustomResult<Vec<(String, masking::Maskable<String>)>, errors::ConnectorError> {
self.build_headers(req, connectors)
}
fn get_url(
&self,
req: &GetSubscriptionEstimateRouterData,
connectors: &Connectors,
) -> CustomResult<String, errors::ConnectorError> {
let metadata: chargebee::ChargebeeMetadata =
utils::to_connector_meta_from_secret(req.connector_meta_data.clone())?;
let url = self
.base_url(connectors)
.to_string()
.replace("{{merchant_endpoint_prefix}}", metadata.site.peek());
Ok(format!("{url}v2/estimates/create_subscription_for_items"))
}
fn get_content_type(&self) -> &'static str {
self.common_get_content_type()
}
fn get_request_body(
&self,
req: &GetSubscriptionEstimateRouterData,
_connectors: &Connectors,
) -> CustomResult<RequestContent, errors::ConnectorError> {
let connector_req = chargebee::ChargebeeSubscriptionEstimateRequest::try_from(req)?;
Ok(RequestContent::FormUrlEncoded(Box::new(connector_req)))
}
fn build_request(
&self,
req: &GetSubscriptionEstimateRouterData,
connectors: &Connectors,
) -> CustomResult<Option<Request>, errors::ConnectorError> {
Ok(Some(
RequestBuilder::new()
.method(Method::Post)
.url(&types::GetSubscriptionEstimateType::get_url(
self, req, connectors,
)?)
.attach_default_headers()
.headers(types::GetSubscriptionEstimateType::get_headers(
self, req, connectors,
)?)
.set_body(types::GetSubscriptionEstimateType::get_request_body(
self, req, connectors,
)?)
.build(),
))
}
fn handle_response(
&self,
data: &GetSubscriptionEstimateRouterData,
event_builder: Option<&mut ConnectorEvent>,
res: Response,
) -> CustomResult<GetSubscriptionEstimateRouterData, errors::ConnectorError> {
let response: chargebee::SubscriptionEstimateResponse = res
.response
.parse_struct("chargebee SubscriptionEstimateResponse")
.change_context(errors::ConnectorError::ResponseDeserializationFailed)?;
event_builder.map(|i| i.set_response_body(&response));
router_env::logger::info!(connector_response=?response);
RouterData::try_from(ResponseRouterData {
response,
data: data.clone(),
http_code: res.status_code,
})
}
fn get_error_response(
&self,
res: Response,
event_builder: Option<&mut ConnectorEvent>,
) -> CustomResult<ErrorResponse, errors::ConnectorError> {
self.build_error_response(res, event_builder)
}
}

impl api::subscriptions::GetSubscriptionPlansFlow for Chargebee {}

impl
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ use hyperswitch_domain_models::{
router_request_types::{revenue_recovery::RevenueRecoveryRecordBackRequest, ResponseId},
router_response_types::{
revenue_recovery::RevenueRecoveryRecordBackResponse,
subscriptions::GetSubscriptionPlansResponse, PaymentsResponseData, RefundsResponseData,
subscriptions::{
GetSubscriptionEstimateResponse, GetSubscriptionPlansResponse, SubscriptionLineItem,
},
PaymentsResponseData, RefundsResponseData,
},
types::{
GetSubscriptionEstimateRouterData, PaymentsAuthorizeRouterData, RefundsRouterData,
RevenueRecoveryRecordBackRouterData,
},
types::{PaymentsAuthorizeRouterData, RefundsRouterData, RevenueRecoveryRecordBackRouterData},
};
use hyperswitch_interfaces::errors;
use masking::Secret;
Expand Down Expand Up @@ -798,6 +804,50 @@ pub struct ChargebeeItem {
pub description: Option<String>,
}

impl<F, T>
TryFrom<ResponseRouterData<F, SubscriptionEstimateResponse, T, GetSubscriptionEstimateResponse>>
for RouterData<F, T, GetSubscriptionEstimateResponse>
{
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(
item: ResponseRouterData<
F,
SubscriptionEstimateResponse,
T,
GetSubscriptionEstimateResponse,
>,
) -> Result<Self, Self::Error> {
let estimate = item.response.estimate;
Ok(Self {
response: Ok(GetSubscriptionEstimateResponse {
sub_total: estimate.invoice_estimate.sub_total,
total: estimate.invoice_estimate.total,
amount_paid: Some(estimate.invoice_estimate.amount_paid),
amount_due: Some(estimate.invoice_estimate.amount_due),
currency: estimate.subscription_estimate.currency_code,
next_billing_at: estimate.subscription_estimate.next_billing_at,
credits_applied: Some(estimate.invoice_estimate.credits_applied),
line_items: estimate
.invoice_estimate
.line_items
.into_iter()
.map(|line_item| SubscriptionLineItem {
item_id: line_item.entity_id,
item_type: line_item.entity_type,
description: line_item.description,
amount: line_item.amount,
currency: estimate.invoice_estimate.currency_code,
unit_amount: Some(line_item.unit_amount),
quantity: line_item.quantity,
pricing_model: Some(line_item.pricing_model),
})
.collect(),
}),
..item.data
})
}
}

impl<F, T>
TryFrom<ResponseRouterData<F, ChargebeeListPlansResponse, T, GetSubscriptionPlansResponse>>
for RouterData<F, T, GetSubscriptionPlansResponse>
Expand All @@ -824,3 +874,75 @@ impl<F, T>
})
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChargebeeSubscriptionEstimateRequest {
pub price_id: String,
}

impl TryFrom<&GetSubscriptionEstimateRouterData> for ChargebeeSubscriptionEstimateRequest {
type Error = error_stack::Report<errors::ConnectorError>;
fn try_from(item: &GetSubscriptionEstimateRouterData) -> Result<Self, Self::Error> {
let price_id = item.request.price_id.to_owned();
Ok(Self { price_id })
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubscriptionEstimateResponse {
pub estimate: ChargebeeEstimate,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ChargebeeEstimate {
pub created_at: i64,
pub object: String,
pub subscription_estimate: SubscriptionEstimate,
pub invoice_estimate: InvoiceEstimate,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubscriptionEstimate {
pub status: String,
#[serde(default, with = "common_utils::custom_serde::timestamp::option")]
pub next_billing_at: Option<PrimitiveDateTime>,
pub object: String,
pub currency_code: enums::Currency,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvoiceEstimate {
pub recurring: bool,
pub date: i64,
pub price_type: String,
pub sub_total: i64,
pub total: i64,
pub credits_applied: i64,
pub amount_paid: i64,
pub amount_due: i64,
pub object: String,
pub customer_id: String,
pub line_items: Vec<LineItem>,
pub currency_code: enums::Currency,
pub round_off_amount: i64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LineItem {
pub id: String,
pub date_from: i64,
pub date_to: i64,
pub unit_amount: i64,
pub quantity: i64,
pub amount: i64,
pub pricing_model: String,
pub is_taxed: bool,
pub tax_amount: i64,
pub object: String,
pub customer_id: String,
pub description: String,
pub entity_type: String,
pub entity_id: String,
pub discount_amount: i64,
pub item_level_discount_amount: i64,
}
25 changes: 21 additions & 4 deletions crates/hyperswitch_connectors/src/connectors/recurly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,27 @@ use error_stack::report;
use error_stack::ResultExt;
use hyperswitch_domain_models::{
router_data::{ConnectorAuthType, ErrorResponse},
router_data_v2::{flow_common_types::GetSubscriptionPlansData, UasFlowData},
router_data_v2::{
flow_common_types::{GetSubscriptionEstimateData, GetSubscriptionPlansData},
UasFlowData,
},
router_flow_types::{
subscriptions::GetSubscriptionPlans,
subscriptions::{GetSubscriptionEstimate, GetSubscriptionPlans},
unified_authentication_service::{
Authenticate, AuthenticationConfirmation, PostAuthenticate, PreAuthenticate,
},
},
router_request_types::{
subscriptions::GetSubscriptionPlansRequest,
subscriptions::{GetSubscriptionEstimateRequest, GetSubscriptionPlansRequest},
unified_authentication_service::{
UasAuthenticationRequestData, UasAuthenticationResponseData,
UasConfirmationRequestData, UasPostAuthenticationRequestData,
UasPreAuthenticationRequestData,
},
},
router_response_types::subscriptions::GetSubscriptionPlansResponse,
router_response_types::subscriptions::{
GetSubscriptionEstimateResponse, GetSubscriptionPlansResponse,
},
};
#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
use hyperswitch_domain_models::{
Expand Down Expand Up @@ -141,6 +146,7 @@ impl
impl api::revenue_recovery_v2::RevenueRecoveryV2 for Recurly {}
impl api::subscriptions_v2::SubscriptionsV2 for Recurly {}
impl api::subscriptions_v2::GetSubscriptionPlansV2 for Recurly {}
impl api::subscriptions_v2::GetSubscriptionEstimateV2 for Recurly {}

impl
ConnectorIntegrationV2<
Expand All @@ -151,6 +157,17 @@ impl
> for Recurly
{
}

impl
ConnectorIntegrationV2<
GetSubscriptionEstimate,
GetSubscriptionEstimateData,
GetSubscriptionEstimateRequest,
GetSubscriptionEstimateResponse,
> for Recurly
{
}

#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
impl api::revenue_recovery_v2::RevenueRecoveryRecordBackV2 for Recurly {}
#[cfg(all(feature = "v2", feature = "revenue_recovery"))]
Expand Down
Loading
Loading