Kind: Service
Source: atloria-monorepo/apps/api/src/billing/billing.service.ts
BillingService encapsulates backend billing operations for the API, including creating hosted checkout and customer portal sessions, processing payment-provider webhooks, and returning billing summaries. It acts as the application boundary between billing-related API endpoints and the external payment provider.
Methods
| Method | Signature | Returns | Description |
|---|---|---|---|
createCheckoutSession | createCheckoutSession(user: JwtPayload, plan: BillingPlanKey) | Promise<{ url: string }> | Create a Stripe Checkout Session for a subscription to 'pro' |
createPortalSession | createPortalSession(user: JwtPayload) | Promise<{ url: string }> | Create a Stripe Billing Portal session so customers can manage/cancel. |
handleWebhook | handleWebhook(rawBody: Buffer, signature: string) | Promise<{ received: boolean }> | Handle Stripe webhook events (signature-verified against the raw body). |
getSummary | getSummary(user: JwtPayload) | unknown | Plan + credits + usage summary for the billing settings page. |
Dependencies
PrismaServiceConfigServiceAIUsageServiceStripe
Where it refuses work
BillingServicestops the work withNotFoundExceptionwhen!org— “Organization not found”, in 3 places.BillingServicestops the work withBadRequestExceptionwhen!envVar.BillingServicestops the work withBadRequestExceptionwhen!priceId.BillingServicestops the work withBadRequestExceptionwhen!session.url— “Stripe did not return a checkout URL”.BillingServicestops the work withBadRequestExceptionwhen!org.stripeCustomerId— “No billing account yet — subscribe to a plan first.”.BillingServicestops the work withBadRequestExceptionwhen!webhookSecret— “STRIPE_WEBHOOK_SECRET is not configured”.
When something fails
BillingServicehandles failure in 1 place: it lets it reach the caller in all 1.
Diagram
mermaidsequenceDiagram participant Client participant API as Billing Controller participant Service as BillingService participant Provider as Payment Provider Client->>API: Request checkout or portal session API->>Service: createCheckoutSession() / createPortalSession() Service->>Provider: Create hosted session Provider-->>Service: Session URL Service-->>API: { url } API-->>Client: Redirect URL Provider->>API: Webhook event API->>Service: handleWebhook() Service->>Provider: Validate/process event Service-->>API: { received: true }
Usage
tsimport { Controller, Get, Post } from '@nestjs/common';
import { BillingService } from './billing.service';
@Controller('billing')
export class BillingController {
constructor(private readonly billingService: BillingService) {}
@Post('checkout')
createCheckout() {
return this.billingService.createCheckoutSession();
}
@Post('portal')
createPortal() {
return this.billingService.createPortalSession();
}
@Get('summary')
getSummary() {
return this.billingService.getSummary();
}
}
AI Coding Instructions
- Keep payment-provider calls inside
BillingService; controllers should only validate requests and delegate to the service. - Return hosted session URLs using the established
{ url: string }response shape for checkout and portal flows. - Treat
handleWebhook()as a security-sensitive integration point: preserve raw payload handling and signature verification requirements. - Make webhook processing idempotent so repeated provider events do not create duplicate billing state changes.
- Update
getSummary()when billing state, subscription fields, or provider response mappings change.
Relationships
- DEPENDS_ON →
PrismaService - DEPENDS_ON →
configservice - DEPENDS_ON →
AIUsageService - DEPENDS_ON →
stripe
Referenced By
BillingController(DEPENDS_ON)BillingModule(MODULE_PROVIDES)BillingModule(MODULE_EXPORTS)
Was this page helpful?