apps/recallassess/recallassess-api/src/api/client/subscription/subscription.controller.ts
api/client/subscription
Methods |
|
| Async confirmChange | |||||||||
confirmChange(auth: CLAuthData, dto: SubscriptionConfirmRequestDto)
|
|||||||||
Decorators :
@Post('confirm')
|
|||||||||
|
Parameters :
Returns :
Promise<SubscriptionConfirmResponseDto>
|
| Async createPaymentMethodPortal | ||||||
createPaymentMethodPortal(auth: CLAuthData)
|
||||||
Decorators :
@Post('payment-method')
|
||||||
|
Parameters :
|
| Async createStripeBillingSubscription | ||||||
createStripeBillingSubscription(auth: CLAuthData)
|
||||||
Decorators :
@Post('stripe/create-billing-subscription')
|
||||||
|
Parameters :
|
| Async downloadInvoicePdf | |||||||||||||||
downloadInvoicePdf(auth: CLAuthData, invoiceNumber: string, authorization: string | undefined, reply: FastifyReply)
|
|||||||||||||||
Decorators :
@Get('invoice/:invoiceNumber/download-pdf')
|
|||||||||||||||
|
Parameters :
Returns :
Promise<FastifyReply>
|
| Async getBillingHistory | ||||||
getBillingHistory(auth: CLAuthData)
|
||||||
Decorators :
@Get('billing-history')
|
||||||
|
Get billing history for the authenticated company
Parameters :
|
| Async getInvoiceDownloadUrl | ||||||||||||
getInvoiceDownloadUrl(auth: CLAuthData, invoiceNumber: string, authorization: string | undefined)
|
||||||||||||
Decorators :
@Get('invoice/:invoiceNumber')
|
||||||||||||
|
Get invoice download URL by invoice number (company-scoped)
Parameters :
|
| Async getOverview | ||||||
getOverview(auth: CLAuthData)
|
||||||
Decorators :
@Get()
|
||||||
|
Parameters :
Returns :
Promise<SubscriptionOverviewResponseDto>
|
| Async getPackages | ||||||
getPackages(auth: CLAuthData)
|
||||||
Decorators :
@Get('packages')
|
||||||
|
Parameters :
Returns :
Promise<SubscriptionPackageDto[]>
|
| Async previewChange | |||||||||
previewChange(auth: CLAuthData, dto: SubscriptionPreviewRequestDto)
|
|||||||||
Decorators :
@Post('preview')
|
|||||||||
|
Parameters :
Returns :
Promise<SubscriptionPreviewResponseDto>
|
| Async reactivateStripeSubscriptionAfterScheduledCancel | ||||||
reactivateStripeSubscriptionAfterScheduledCancel(auth: CLAuthData)
|
||||||
Decorators :
@Post('stripe/reactivate-subscription')
|
||||||
|
Parameters :
|
| Async retryStripePayment | ||||||
retryStripePayment(auth: CLAuthData)
|
||||||
Decorators :
@Post('stripe/retry-payment')
|
||||||
|
Parameters :
|
| Async scheduleCancelAtPeriodEnd | ||||||
scheduleCancelAtPeriodEnd(auth: CLAuthData)
|
||||||
Decorators :
@Post('cancel-at-period-end')
|
||||||
|
Parameters :
|
| Async syncBillingHistoryFromStripe | ||||||
syncBillingHistoryFromStripe(auth: CLAuthData)
|
||||||
Decorators :
@Post('billing-history/sync-from-stripe')
|
||||||
|
Parameters :
Returns :
Promise<literal type>
|
import { bnestPlainToDto } from "@bish-nest/core";
import { Body, Controller, Get, Headers, HttpCode, HttpStatus, Param, Post, Res } from "@nestjs/common";
import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger";
import { FastifyReply } from "fastify";
import { CLAuthData, ClientAuth } from "../../shared/decorators/client-auth.decorator";
import {
SubscriptionBillingHistoryResponseDto,
SubscriptionCancelAtPeriodEndResponseDto,
SubscriptionConfirmRequestDto,
SubscriptionConfirmResponseDto,
SubscriptionInvoiceDownloadResponseDto,
SubscriptionOverviewResponseDto,
SubscriptionPackageDto,
SubscriptionPaymentMethodResponseDto,
SubscriptionPreviewRequestDto,
SubscriptionPreviewResponseDto,
SubscriptionRetryPaymentResponseDto,
SubscriptionStripeBillingCreateResponseDto,
} from "./dto";
import { CLSubscriptionService } from "./subscription.service";
@ApiTags("Client - Subscription")
@Controller("api/client/subscription")
export class CLSubscriptionController {
constructor(private readonly subscriptionService: CLSubscriptionService) {}
@Get()
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Get current subscription overview" })
@ApiResponse({
status: 200,
description: "Returns current subscription overview for the authenticated company",
type: SubscriptionOverviewResponseDto,
})
async getOverview(@ClientAuth() auth: CLAuthData): Promise<SubscriptionOverviewResponseDto> {
return this.subscriptionService.getSubscriptionOverview(auth.companyId);
}
@Get("packages")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "List subscription packages" })
@ApiResponse({
status: 200,
description: "Returns all active subscription packages with current plan highlighted",
type: [SubscriptionPackageDto],
})
async getPackages(@ClientAuth() auth: CLAuthData): Promise<SubscriptionPackageDto[]> {
return this.subscriptionService.getAvailablePackages(auth.companyId);
}
@Post("preview")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Preview subscription change impact" })
@ApiResponse({
status: 200,
description: "Returns a preview of the financial impact for a subscription change",
type: SubscriptionPreviewResponseDto,
})
async previewChange(
@ClientAuth() auth: CLAuthData,
@Body() dto: SubscriptionPreviewRequestDto,
): Promise<SubscriptionPreviewResponseDto> {
return this.subscriptionService.previewSubscriptionChange(auth.companyId, dto);
}
@Post("confirm")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Confirm subscription change and create payment checkout" })
@ApiResponse({
status: 200,
description: "Returns Stripe checkout URL for payment",
type: SubscriptionConfirmResponseDto,
})
async confirmChange(
@ClientAuth() auth: CLAuthData,
@Body() dto: SubscriptionConfirmRequestDto,
): Promise<SubscriptionConfirmResponseDto> {
return this.subscriptionService.confirmSubscriptionChange(auth.companyId, dto);
}
@Post("payment-method")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Create Stripe billing portal session for payment method update" })
@ApiResponse({
status: 200,
description: "Returns Stripe billing portal URL for payment method updates",
type: SubscriptionPaymentMethodResponseDto,
})
async createPaymentMethodPortal(@ClientAuth() auth: CLAuthData): Promise<SubscriptionPaymentMethodResponseDto> {
return this.subscriptionService.createPaymentMethodPortal(auth.companyId);
}
@Post("cancel-at-period-end")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Schedule subscription cancellation at end of current Stripe billing period" })
@ApiResponse({
status: 200,
description: "Stripe subscription updated; local subscription flag set",
type: SubscriptionCancelAtPeriodEndResponseDto,
})
async scheduleCancelAtPeriodEnd(
@ClientAuth() auth: CLAuthData,
): Promise<SubscriptionCancelAtPeriodEndResponseDto> {
return this.subscriptionService.scheduleCancelSubscriptionAtPeriodEnd(auth.companyId);
}
@Post("stripe/create-billing-subscription")
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: "Create or replace Stripe Billing subscription from local paid subscription",
})
@ApiResponse({
status: 200,
description: "Stripe subscription synced from local plan row",
type: SubscriptionStripeBillingCreateResponseDto,
})
async createStripeBillingSubscription(
@ClientAuth() auth: CLAuthData,
): Promise<SubscriptionStripeBillingCreateResponseDto> {
return this.subscriptionService.createStripeBillingSubscription(auth.companyId);
}
@Post("stripe/reactivate-subscription")
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: "Remove cancel-at-period-end so the existing Stripe subscription renews",
})
@ApiResponse({
status: 200,
description: "Stripe subscription updated; local cancel-at-period-end flag cleared",
type: SubscriptionCancelAtPeriodEndResponseDto,
})
async reactivateStripeSubscriptionAfterScheduledCancel(
@ClientAuth() auth: CLAuthData,
): Promise<SubscriptionCancelAtPeriodEndResponseDto> {
return this.subscriptionService.reactivateStripeSubscriptionAfterScheduledCancel(auth.companyId);
}
@Post("stripe/retry-payment")
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: "Retry paying the latest Stripe invoice for the existing subscription",
})
@ApiResponse({
status: 200,
description: "Attempts to pay the latest invoice off-session using the saved default card",
type: SubscriptionRetryPaymentResponseDto,
})
async retryStripePayment(@ClientAuth() auth: CLAuthData): Promise<SubscriptionRetryPaymentResponseDto> {
return this.subscriptionService.retryStripeSubscriptionPayment(auth.companyId);
}
/**
* Get billing history for the authenticated company
*/
@Get("billing-history")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Get billing history" })
@ApiResponse({
status: 200,
description: "Returns billing history for the authenticated company",
type: SubscriptionBillingHistoryResponseDto,
})
async getBillingHistory(@ClientAuth() auth: CLAuthData): Promise<SubscriptionBillingHistoryResponseDto> {
return this.subscriptionService.getBillingHistory(auth.companyId);
}
@Post("billing-history/sync-from-stripe")
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: "Import missing invoices and refunds from Stripe for the current company",
})
@ApiResponse({
status: 200,
description: "Stripe sync result; reload billing history to see new rows",
})
async syncBillingHistoryFromStripe(
@ClientAuth() auth: CLAuthData,
): Promise<{ success: boolean; message: string; invoices_created: number; refunds_created: number }> {
return this.subscriptionService.syncBillingHistoryFromStripe(auth.companyId);
}
/**
* Get invoice download URL by invoice number (company-scoped)
*/
@Get("invoice/:invoiceNumber")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Get invoice download URL" })
@ApiResponse({
status: 200,
description: "Returns a signed URL for the invoice PDF",
type: SubscriptionInvoiceDownloadResponseDto,
})
async getInvoiceDownloadUrl(
@ClientAuth() auth: CLAuthData,
@Param("invoiceNumber") invoiceNumber: string,
@Headers("authorization") authorization: string | undefined,
): Promise<SubscriptionInvoiceDownloadResponseDto> {
const url = await this.subscriptionService.getInvoiceDownloadUrl(auth.companyId, invoiceNumber, authorization);
return bnestPlainToDto({ url }, SubscriptionInvoiceDownloadResponseDto);
}
@Get("invoice/:invoiceNumber/download-pdf")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Download invoice PDF" })
@ApiResponse({
status: 200,
description: "Streams invoice PDF as attachment",
})
async downloadInvoicePdf(
@ClientAuth() auth: CLAuthData,
@Param("invoiceNumber") invoiceNumber: string,
@Headers("authorization") authorization: string | undefined,
@Res() reply: FastifyReply,
): Promise<FastifyReply> {
const { buffer, filename } = await this.subscriptionService.getInvoicePdfBuffer(
auth.companyId,
invoiceNumber,
authorization,
);
const safeName = filename.replace(/"/g, "%22");
reply.header("Content-Type", "application/pdf");
reply.header("Content-Disposition", `attachment; filename="${safeName}"`);
return reply.send(buffer);
}
}