Add Workspace Created and Payment Received ClickHouse events (#20277)

## Summary
- Adds two new workspace audit events for the AARRR funnel tracked in
ClickHouse
- **Workspace Created**: emitted in `signUpOnNewWorkspace` after the
transaction commits, capturing every new workspace creation
- **Payment Received**: emitted in `processInvoicePaid` on every Stripe
`invoice.paid` webhook, with `stripeInvoiceId`, `amountPaid`, and
`billingReason` properties. First payment per workspace can be derived
at query time via `min(timestamp)` grouped by `workspaceId`

## Test plan
- [x] Verify `Workspace Created` event appears in ClickHouse after
signing up on a new workspace
- [x] Verify `Payment Received` event appears in ClickHouse after a
Stripe `invoice.paid` webhook fires
- [x] Confirm no event is emitted if the billing customer cannot be
resolved from `stripeCustomerId`
- [x] Run existing `SignInUpService` unit tests pass with the new
`AuditService` mock


Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Thomas Trompette
2026-05-07 09:57:30 +02:00
committed by GitHub
parent be4b466234
commit eddd2c979a
7 changed files with 72 additions and 11 deletions
@@ -38,6 +38,14 @@ import {
type WEBHOOK_RESPONSE_EVENT,
type WebhookResponseTrackEvent,
} from 'src/engine/core-modules/audit/utils/events/workspace-event/webhook/webhook-response';
import {
type PAYMENT_RECEIVED_EVENT,
type PaymentReceivedTrackEvent,
} from 'src/engine/core-modules/audit/utils/events/workspace-event/billing/payment-received';
import {
type WORKSPACE_CREATED_EVENT,
type WorkspaceCreatedTrackEvent,
} from 'src/engine/core-modules/audit/utils/events/workspace-event/workspace/workspace-created';
import {
type WORKSPACE_ENTITY_CREATED_EVENT,
type WorkspaceEntityCreatedTrackEvent,
@@ -55,7 +63,9 @@ export type TrackEventName =
| typeof OBJECT_RECORD_UPDATED_EVENT
| typeof OBJECT_RECORD_DELETED_EVENT
| typeof OBJECT_RECORD_UPSERTED_EVENT
| typeof USER_SIGNUP_EVENT;
| typeof USER_SIGNUP_EVENT
| typeof WORKSPACE_CREATED_EVENT
| typeof PAYMENT_RECEIVED_EVENT;
// Map event names to their corresponding event types
export interface TrackEvents {
@@ -70,6 +80,8 @@ export interface TrackEvents {
[OBJECT_RECORD_CREATED_EVENT]: ObjectRecordCreatedTrackEvent;
[OBJECT_RECORD_UPDATED_EVENT]: ObjectRecordUpdatedTrackEvent;
[OBJECT_RECORD_UPSERTED_EVENT]: ObjectRecordUpsertedTrackEvent;
[WORKSPACE_CREATED_EVENT]: WorkspaceCreatedTrackEvent;
[PAYMENT_RECEIVED_EVENT]: PaymentReceivedTrackEvent;
}
export type TrackEventProperties<T extends TrackEventName> =
@@ -0,0 +1,15 @@
import { z } from 'zod';
import { registerEvent } from 'src/engine/core-modules/audit/utils/events/workspace-event/track';
export const PAYMENT_RECEIVED_EVENT = 'Payment Received' as const;
export const paymentReceivedSchema = z.strictObject({
event: z.literal(PAYMENT_RECEIVED_EVENT),
properties: z.strictObject({
amountPaid: z.number(),
}),
});
export type PaymentReceivedTrackEvent = z.infer<typeof paymentReceivedSchema>;
registerEvent(PAYMENT_RECEIVED_EVENT, paymentReceivedSchema);
@@ -0,0 +1,13 @@
import { z } from 'zod';
import { registerEvent } from 'src/engine/core-modules/audit/utils/events/workspace-event/track';
export const WORKSPACE_CREATED_EVENT = 'Workspace Created' as const;
export const workspaceCreatedSchema = z.strictObject({
event: z.literal(WORKSPACE_CREATED_EVENT),
properties: z.strictObject({}),
});
export type WorkspaceCreatedTrackEvent = z.infer<typeof workspaceCreatedSchema>;
registerEvent(WORKSPACE_CREATED_EVENT, workspaceCreatedSchema);