File
Description
GET /api/client/subscription — only @Expose() fields are serialized (global ClassSerializerInterceptor).
Stripe-related fields stay on the wire for portal billing UI (cancel-at-period-end, upcoming invoice, etc.).
|
auto_payment_enabled
|
Type : boolean
|
Decorators :
@Expose() @ApiProperty()
|
|
|
|
billing_cycle
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
color
|
Type : string
|
Decorators :
@Expose() @ApiProperty()
|
|
|
|
company_country
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
Company country (ISO or stored value); used for VAT display on portal invoice preview
|
|
has_paid_current_period
|
Type : boolean | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
DEFINITIVE paid-customer signal.
True iff the customer has paid a real (non-zero) invoice for the current
subscription period. Replaces the duration-based heuristic for detecting
"expired-recovery" trialing subscriptions — a Start-Up customer who has
already paid will have this true regardless of trial_end value, even if
the date was admin-edited or set by the recovery flow.
Computed from subscription.latest_invoice.amount_paid > 0 (or status === 'paid').
Frontend should treat true as "show Active plan", false as "real trial /
unpaid period", and null as "data unavailable — fall back to heuristic".
|
|
has_stripe_customer
|
Type : boolean
|
Decorators :
@Expose() @ApiProperty()
|
|
|
True when company has a Stripe customer id (required to create/sync billing subscription)
|
|
icon
|
Type : string
|
Decorators :
@Expose() @ApiProperty()
|
|
|
|
is_subscription_expiry
|
Type : boolean
|
Decorators :
@Expose() @ApiProperty()
|
|
|
Company flag: subscription access ended (e.g. expired plan)
|
|
license_count
|
Type : number
|
Decorators :
@Expose() @ApiProperty()
|
|
|
|
monthly_subtotal
|
Type : number
|
Decorators :
@Expose() @ApiProperty()
|
|
|
|
next_billing_amount
|
Type : number | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true, description: 'Amount charged on next_billing_date (monthly_subtotal × billing cycle multiplier)'})
|
|
|
|
next_billing_date
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
package_name
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
payment_method_brand
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
payment_method_exp_month
|
Type : number | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
payment_method_exp_year
|
Type : number | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
payment_method_last4
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
plan_type
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
price_per_license
|
Type : number | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
stripe_cancel_at_period_end
|
Type : boolean | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
stripe_collection_method
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
charge_automatically | send_invoice
|
|
stripe_current_period_end
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
stripe_current_period_start
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
stripe_next_charge_date
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
Convenience field — the date the customer's card will next be charged.
Computed server-side: trial_end for trialing subs in the future, otherwise
current_period_end. Frontend should prefer this over current_period_end
for "next renewal / next charge" labelling.
|
|
stripe_next_invoice_amount
|
Type : number | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
Next Stripe invoice total (major units), from invoice preview; null if none
|
|
stripe_next_invoice_currency
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
stripe_subscription_id
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
Stripe Billing subscription id when synced; null if checkout-only / not created yet
|
|
stripe_subscription_status
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
|
stripe_trial_end
|
Type : string | null
|
Decorators :
@Expose() @ApiPropertyOptional({nullable: true})
|
|
|
If the subscription is on a trial, this is the trial cutoff date (ISO).
For trialing subs the next charge happens at this date — not at
stripe_current_period_end. Frontend should surface trial state when this
is set + stripe_subscription_status === "trialing".
|
|
vip_first_invoice_offer_applies
|
Type : boolean
|
Decorators :
@Expose()
|
|
|
VIP offer flag (first Stripe invoice only):
true when the current plan is billable and the previous local subscription was PRIVATE_VIP_TRIAL.
The portal uses this to show a "VIP offer (one-time)" discount line in the billing details dialog.
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Exclude, Expose } from "class-transformer";
/**
* GET /api/client/subscription — only @Expose() fields are serialized (global ClassSerializerInterceptor).
* Stripe-related fields stay on the wire for portal billing UI (cancel-at-period-end, upcoming invoice, etc.).
*/
@Exclude()
export class SubscriptionOverviewResponseDto {
@Expose()
@ApiPropertyOptional({ nullable: true })
plan_type!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
package_name!: string | null;
@Expose()
@ApiProperty()
license_count!: number;
@Expose()
@ApiPropertyOptional({ nullable: true })
price_per_license!: number | null;
@Expose()
@ApiProperty()
monthly_subtotal!: number;
@Expose()
@ApiPropertyOptional({ nullable: true })
next_billing_date!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
payment_method_last4!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
payment_method_brand!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
payment_method_exp_month!: number | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
payment_method_exp_year!: number | null;
@Expose()
@ApiProperty()
auto_payment_enabled!: boolean;
@Expose()
@ApiProperty()
color!: string;
@Expose()
@ApiProperty()
icon!: string;
@Expose()
@ApiPropertyOptional({ nullable: true })
billing_cycle!: string | null;
/** Company country (ISO or stored value); used for VAT display on portal invoice preview */
@Expose()
@ApiPropertyOptional({ nullable: true })
company_country!: string | null;
@Expose()
@ApiPropertyOptional({
nullable: true,
description: "Amount charged on next_billing_date (monthly_subtotal × billing cycle multiplier)",
})
next_billing_amount!: number | null;
/** Stripe Billing subscription id when synced; null if checkout-only / not created yet */
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_subscription_id!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_subscription_status!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_current_period_start!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_current_period_end!: string | null;
/**
* If the subscription is on a trial, this is the trial cutoff date (ISO).
* For trialing subs the *next charge* happens at this date — not at
* stripe_current_period_end. Frontend should surface trial state when this
* is set + stripe_subscription_status === "trialing".
*/
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_trial_end!: string | null;
/**
* Convenience field — the date the customer's card will next be charged.
* Computed server-side: trial_end for trialing subs in the future, otherwise
* current_period_end. Frontend should prefer this over current_period_end
* for "next renewal / next charge" labelling.
*/
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_next_charge_date!: string | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_cancel_at_period_end!: boolean | null;
/** charge_automatically | send_invoice */
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_collection_method!: string | null;
/** Next Stripe invoice total (major units), from invoice preview; null if none */
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_next_invoice_amount!: number | null;
@Expose()
@ApiPropertyOptional({ nullable: true })
stripe_next_invoice_currency!: string | null;
/**
* DEFINITIVE paid-customer signal.
*
* True iff the customer has paid a real (non-zero) invoice for the current
* subscription period. Replaces the duration-based heuristic for detecting
* "expired-recovery" trialing subscriptions — a Start-Up customer who has
* already paid will have this true regardless of trial_end value, even if
* the date was admin-edited or set by the recovery flow.
*
* Computed from subscription.latest_invoice.amount_paid > 0 (or status === 'paid').
*
* Frontend should treat true as "show Active plan", false as "real trial /
* unpaid period", and null as "data unavailable — fall back to heuristic".
*/
@Expose()
@ApiPropertyOptional({ nullable: true })
has_paid_current_period!: boolean | null;
/** Company flag: subscription access ended (e.g. expired plan) */
@Expose()
@ApiProperty()
is_subscription_expiry!: boolean;
/** True when company has a Stripe customer id (required to create/sync billing subscription) */
@Expose()
@ApiProperty()
has_stripe_customer!: boolean;
/**
* VIP offer flag (first Stripe invoice only):
* true when the current plan is billable and the previous local subscription was PRIVATE_VIP_TRIAL.
* The portal uses this to show a "VIP offer (one-time)" discount line in the billing details dialog.
*/
@Expose()
vip_first_invoice_offer_applies!: boolean;
}