Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
2dc967b
feat(subscription): add update subscription request and handler with …
GauravRawat369 Oct 8, 2025
ba5b596
Merge branch 'main' of https://github.com/juspay/hyperswitch into add…
GauravRawat369 Oct 8, 2025
9208c5b
feat(subscription): add payment update s2s call and complete update s…
GauravRawat369 Oct 9, 2025
c474cbc
Merge branch 'main' of https://github.com/juspay/hyperswitch into add…
GauravRawat369 Oct 9, 2025
ca45b14
feat(subscription): add item_price_id and plan_id to subscription req…
GauravRawat369 Oct 9, 2025
5734f3f
Merge branch 'main' of https://github.com/juspay/hyperswitch into add…
GauravRawat369 Oct 9, 2025
be843d1
feat(subscription): add plan_id and price_id to Subscription and Subs…
GauravRawat369 Oct 9, 2025
07f3c99
feat(subscription): fix clippy
GauravRawat369 Oct 9, 2025
a396c3d
chore: run formatter
hyperswitch-bot[bot] Oct 9, 2025
3d10332
feat(invoice): enhance invoice update requests with new structures an…
GauravRawat369 Oct 10, 2025
da4456a
Merge branch 'main' of https://github.com/juspay/hyperswitch into add…
GauravRawat369 Oct 10, 2025
c6db39d
feat(subscription): rename price_id to item_price_id in subscription …
GauravRawat369 Oct 10, 2025
59d1816
fix(subscription): update down migration to drop item_price_id instea…
GauravRawat369 Oct 10, 2025
121afaf
feat(invoice): reorder fields in InvoiceUpdate struct for consistency…
GauravRawat369 Oct 10, 2025
e3c1468
refactor(invoice): update payment_method_id to use Secret type for en…
GauravRawat369 Oct 13, 2025
2de817a
Merge branch 'main' of https://github.com/juspay/hyperswitch into add…
GauravRawat369 Oct 13, 2025
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
41 changes: 23 additions & 18 deletions crates/api_models/src/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ pub struct CreateSubscriptionRequest {
/// Merchant specific Unique identifier.
pub merchant_reference_id: Option<String>,

/// Identifier for the associated item_price_id for the subscription.
pub item_price_id: Option<String>,

/// Identifier for the subscription plan.
pub plan_id: Option<String>,

Expand Down Expand Up @@ -60,6 +63,9 @@ pub struct SubscriptionResponse {
/// Identifier for the associated subscription plan.
pub plan_id: Option<String>,

/// Identifier for the associated item_price_id for the subscription.
pub item_price_id: Option<String>,

/// Associated profile ID.
pub profile_id: common_utils::id_type::ProfileId,

Expand Down Expand Up @@ -129,6 +135,7 @@ impl SubscriptionResponse {
merchant_reference_id: Option<String>,
status: SubscriptionStatus,
plan_id: Option<String>,
item_price_id: Option<String>,
profile_id: common_utils::id_type::ProfileId,
merchant_id: common_utils::id_type::MerchantId,
client_secret: Option<Secret<String>>,
Expand All @@ -141,6 +148,7 @@ impl SubscriptionResponse {
merchant_reference_id,
status,
plan_id,
item_price_id,
profile_id,
client_secret,
merchant_id,
Expand Down Expand Up @@ -324,28 +332,11 @@ pub struct ConfirmSubscriptionRequest {
/// Client secret for SDK based interaction.
pub client_secret: Option<ClientSecret>,

/// Identifier for the associated plan_id.
pub plan_id: Option<String>,

/// Identifier for the associated item_price_id for the subscription.
pub item_price_id: Option<String>,

/// Idenctifier for the coupon code for the subscription.
pub coupon_code: Option<String>,

/// Payment details for the invoice.
pub payment_details: ConfirmSubscriptionPaymentDetails,
}

impl ConfirmSubscriptionRequest {
pub fn get_item_price_id(&self) -> Result<String, error_stack::Report<ValidationError>> {
self.item_price_id.clone().ok_or(error_stack::report!(
ValidationError::MissingRequiredField {
field_name: "item_price_id".to_string()
}
))
}

pub fn get_billing_address(&self) -> Result<Address, error_stack::Report<ValidationError>> {
self.payment_details
.payment_method_data
Expand Down Expand Up @@ -411,7 +402,7 @@ pub struct ConfirmSubscriptionResponse {
pub plan_id: Option<String>,

/// Identifier for the associated item_price_id for the subscription.
pub price_id: Option<String>,
pub item_price_id: Option<String>,

/// Optional coupon code applied to this subscription.
pub coupon: Option<String>,
Expand Down Expand Up @@ -470,6 +461,20 @@ pub struct Invoice {

impl ApiEventMetric for ConfirmSubscriptionResponse {}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct UpdateSubscriptionRequest {
/// Identifier for the associated plan_id.
pub plan_id: String,
/// Identifier for the associated item_price_id for the subscription.
pub item_price_id: String,
/// Amount to be charged for the invoice.
pub amount: MinorUnit,
/// Currency for the amount.
pub currency: api_enums::Currency,
}

impl ApiEventMetric for UpdateSubscriptionRequest {}

#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, ToSchema)]
pub struct EstimateSubscriptionQuery {
/// Identifier for the associated subscription plan.
Expand Down
10 changes: 8 additions & 2 deletions crates/diesel_models/src/invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ pub struct InvoiceUpdate {
pub connector_invoice_id: Option<common_utils::id_type::InvoiceId>,
pub modified_at: time::PrimitiveDateTime,
pub payment_intent_id: Option<common_utils::id_type::PaymentId>,
pub amount: Option<MinorUnit>,
pub currency: Option<String>,
}

impl InvoiceNew {
Expand Down Expand Up @@ -105,17 +107,21 @@ impl InvoiceNew {

impl InvoiceUpdate {
pub fn new(
amount: Option<MinorUnit>,
currency: Option<String>,
payment_method_id: Option<String>,
status: Option<InvoiceStatus>,
connector_invoice_id: Option<common_utils::id_type::InvoiceId>,
payment_intent_id: Option<common_utils::id_type::PaymentId>,
) -> Self {
Self {
payment_method_id,
status,
payment_method_id,
connector_invoice_id,
payment_intent_id,
modified_at: common_utils::date_time::now(),
payment_intent_id,
amount,
currency,
}
}
}
4 changes: 4 additions & 0 deletions crates/diesel_models/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1606,6 +1606,10 @@ diesel::table! {
profile_id -> Varchar,
#[max_length = 128]
merchant_reference_id -> Nullable<Varchar>,
#[max_length = 128]
plan_id -> Nullable<Varchar>,
#[max_length = 128]
item_price_id -> Nullable<Varchar>,
}
}

Expand Down
4 changes: 4 additions & 0 deletions crates/diesel_models/src/schema_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1543,6 +1543,10 @@ diesel::table! {
profile_id -> Varchar,
#[max_length = 128]
merchant_reference_id -> Nullable<Varchar>,
#[max_length = 128]
plan_id -> Nullable<Varchar>,
#[max_length = 128]
item_price_id -> Nullable<Varchar>,
}
}

Expand Down
18 changes: 16 additions & 2 deletions crates/diesel_models/src/subscription.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pub struct SubscriptionNew {
modified_at: time::PrimitiveDateTime,
profile_id: common_utils::id_type::ProfileId,
merchant_reference_id: Option<String>,
plan_id: Option<String>,
item_price_id: Option<String>,
}

#[derive(
Expand All @@ -43,6 +45,8 @@ pub struct Subscription {
pub modified_at: time::PrimitiveDateTime,
pub profile_id: common_utils::id_type::ProfileId,
pub merchant_reference_id: Option<String>,
pub plan_id: Option<String>,
pub item_price_id: Option<String>,
}

#[derive(Clone, Debug, Eq, PartialEq, AsChangeset, router_derive::DebugAsDisplay, Deserialize)]
Expand All @@ -52,6 +56,8 @@ pub struct SubscriptionUpdate {
pub payment_method_id: Option<String>,
pub status: Option<String>,
pub modified_at: time::PrimitiveDateTime,
pub plan_id: Option<String>,
pub item_price_id: Option<String>,
}

impl SubscriptionNew {
Expand All @@ -69,6 +75,8 @@ impl SubscriptionNew {
metadata: Option<SecretSerdeValue>,
profile_id: common_utils::id_type::ProfileId,
merchant_reference_id: Option<String>,
plan_id: Option<String>,
item_price_id: Option<String>,
) -> Self {
let now = common_utils::date_time::now();
Self {
Expand All @@ -86,6 +94,8 @@ impl SubscriptionNew {
modified_at: now,
profile_id,
merchant_reference_id,
plan_id,
item_price_id,
}
}

Expand All @@ -99,15 +109,19 @@ impl SubscriptionNew {

impl SubscriptionUpdate {
pub fn new(
connector_subscription_id: Option<String>,
payment_method_id: Option<Secret<String>>,
status: Option<String>,
connector_subscription_id: Option<String>,
plan_id: Option<String>,
item_price_id: Option<String>,
) -> Self {
Self {
connector_subscription_id,
payment_method_id: payment_method_id.map(|pmid| pmid.peek().clone()),
status,
connector_subscription_id,
modified_at: common_utils::date_time::now(),
plan_id,
item_price_id,
}
}
}
123 changes: 120 additions & 3 deletions crates/hyperswitch_domain_models/src/invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use common_utils::{
MinorUnit,
},
};
use masking::Secret;
use masking::{PeekInterface, Secret};
use utoipa::ToSchema;

use crate::merchant_key_store::MerchantKeyStore;
Expand Down Expand Up @@ -187,7 +187,114 @@ pub struct InvoiceUpdate {
pub connector_invoice_id: Option<common_utils::id_type::InvoiceId>,
pub modified_at: time::PrimitiveDateTime,
pub payment_intent_id: Option<common_utils::id_type::PaymentId>,
pub amount: Option<MinorUnit>,
pub currency: Option<String>,
}

#[derive(Debug, Clone)]
pub struct AmountAndCurrencyUpdate {
pub amount: MinorUnit,
pub currency: String,
}

#[derive(Debug, Clone)]
pub struct ConnectorAndStatusUpdate {
pub connector_invoice_id: common_utils::id_type::InvoiceId,
pub status: common_enums::connector_enums::InvoiceStatus,
}

#[derive(Debug, Clone)]
pub struct PaymentAndStatusUpdate {
pub payment_method_id: Option<Secret<String>>,
pub payment_intent_id: Option<common_utils::id_type::PaymentId>,
pub status: common_enums::connector_enums::InvoiceStatus,
pub connector_invoice_id: Option<common_utils::id_type::InvoiceId>,
}

/// Enum-based invoice update request for different scenarios
#[derive(Debug, Clone)]
pub enum InvoiceUpdateRequest {
/// Update amount and currency
Amount(AmountAndCurrencyUpdate),
/// Update connector invoice ID and status
Connector(ConnectorAndStatusUpdate),
/// Update payment details along with status
PaymentStatus(PaymentAndStatusUpdate),
}

impl InvoiceUpdateRequest {
/// Create an amount and currency update request
pub fn update_amount_and_currency(amount: MinorUnit, currency: String) -> Self {
Self::Amount(AmountAndCurrencyUpdate { amount, currency })
}

/// Create a connector invoice ID and status update request
pub fn update_connector_and_status(
connector_invoice_id: common_utils::id_type::InvoiceId,
status: common_enums::connector_enums::InvoiceStatus,
) -> Self {
Self::Connector(ConnectorAndStatusUpdate {
connector_invoice_id,
status,
})
}

/// Create a combined payment and status update request
pub fn update_payment_and_status(
payment_method_id: Option<Secret<String>>,
payment_intent_id: Option<common_utils::id_type::PaymentId>,
status: common_enums::connector_enums::InvoiceStatus,
connector_invoice_id: Option<common_utils::id_type::InvoiceId>,
) -> Self {
Self::PaymentStatus(PaymentAndStatusUpdate {
payment_method_id,
payment_intent_id,
status,
connector_invoice_id,
})
}
}

impl From<InvoiceUpdateRequest> for InvoiceUpdate {
fn from(request: InvoiceUpdateRequest) -> Self {
let now = common_utils::date_time::now();

match request {
InvoiceUpdateRequest::Amount(update) => Self {
status: None,
payment_method_id: None,
connector_invoice_id: None,
modified_at: now,
payment_intent_id: None,
amount: Some(update.amount),
currency: Some(update.currency),
},
InvoiceUpdateRequest::Connector(update) => Self {
status: Some(update.status),
payment_method_id: None,
connector_invoice_id: Some(update.connector_invoice_id),
modified_at: now,
payment_intent_id: None,
amount: None,
currency: None,
},
InvoiceUpdateRequest::PaymentStatus(update) => Self {
status: Some(update.status),
payment_method_id: update
.payment_method_id
.as_ref()
.map(|id| id.peek())
.cloned(),
connector_invoice_id: update.connector_invoice_id,
modified_at: now,
payment_intent_id: update.payment_intent_id,
amount: None,
currency: None,
},
}
}
}

#[async_trait::async_trait]
impl super::behaviour::Conversion for InvoiceUpdate {
type DstType = diesel_models::invoice::InvoiceUpdate;
Expand All @@ -200,6 +307,8 @@ impl super::behaviour::Conversion for InvoiceUpdate {
connector_invoice_id: self.connector_invoice_id,
modified_at: self.modified_at,
payment_intent_id: self.payment_intent_id,
amount: self.amount,
currency: self.currency,
})
}

Expand All @@ -218,6 +327,8 @@ impl super::behaviour::Conversion for InvoiceUpdate {
connector_invoice_id: item.connector_invoice_id,
modified_at: item.modified_at,
payment_intent_id: item.payment_intent_id,
amount: item.amount,
currency: item.currency,
})
}

Expand All @@ -228,6 +339,8 @@ impl super::behaviour::Conversion for InvoiceUpdate {
connector_invoice_id: self.connector_invoice_id,
modified_at: self.modified_at,
payment_intent_id: self.payment_intent_id,
amount: self.amount,
currency: self.currency,
})
}
}
Expand All @@ -238,13 +351,17 @@ impl InvoiceUpdate {
status: Option<common_enums::connector_enums::InvoiceStatus>,
connector_invoice_id: Option<common_utils::id_type::InvoiceId>,
payment_intent_id: Option<common_utils::id_type::PaymentId>,
amount: Option<MinorUnit>,
currency: Option<String>,
) -> Self {
Self {
payment_method_id,
status,
payment_method_id,
connector_invoice_id,
modified_at: common_utils::date_time::now(),
payment_intent_id,
connector_invoice_id,
amount,
currency,
}
}
}
Loading
Loading