Skip to content

Commit 914e2ee

Browse files
committed
fix(billing): guard the new subscription-created instrumentation block
Greptile P1: the org-scope check I added for the audit-metadata fix (isSubscriptionOrgScoped) was a raw DB call with no error guard, unlike resolveSubscriptionActorId next to it. Since it ran inside the idempotency lambda with an unconditional rethrow, a transient DB error would abort and retry the whole webhook after the free -> paid usage reset had already committed. Wrap the actor/org-scope resolution + audit + analytics block in try/catch, matching the same guarded instrumentation pattern already used in handleInvoicePaymentFailed.
1 parent e4cc7da commit 914e2ee

1 file changed

Lines changed: 30 additions & 20 deletions

File tree

apps/sim/lib/billing/webhooks/subscription.ts

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -285,28 +285,38 @@ export async function handleSubscriptionCreated(
285285
}
286286

287287
if (wasFreePreviously && isPaidPlan) {
288-
const actorId = await resolveSubscriptionActorId(subscriptionData.referenceId)
289-
const isOrgScoped = await isSubscriptionOrgScoped({
290-
referenceId: subscriptionData.referenceId,
291-
})
292-
recordAudit({
293-
actorId,
294-
action: AuditAction.SUBSCRIPTION_CREATED,
295-
resourceType: AuditResourceType.SUBSCRIPTION,
296-
resourceId: subscriptionData.id,
297-
description: `Subscription created on ${subscriptionData.plan ?? 'unknown'} plan for ${subscriptionData.referenceId}`,
298-
metadata: {
299-
plan: subscriptionData.plan,
288+
// Best-effort instrumentation; a transient DB error here must never abort
289+
// the (already-committed) free -> paid usage reset above, so it's guarded.
290+
try {
291+
const actorId = await resolveSubscriptionActorId(subscriptionData.referenceId)
292+
const isOrgScoped = await isSubscriptionOrgScoped({
293+
referenceId: subscriptionData.referenceId,
294+
})
295+
recordAudit({
296+
actorId,
297+
action: AuditAction.SUBSCRIPTION_CREATED,
298+
resourceType: AuditResourceType.SUBSCRIPTION,
299+
resourceId: subscriptionData.id,
300+
description: `Subscription created on ${subscriptionData.plan ?? 'unknown'} plan for ${subscriptionData.referenceId}`,
301+
metadata: {
302+
plan: subscriptionData.plan,
303+
status: subscriptionData.status,
304+
referenceId: subscriptionData.referenceId,
305+
...(isOrgScoped ? { organizationId: subscriptionData.referenceId } : {}),
306+
},
307+
})
308+
captureServerEvent(subscriptionData.referenceId, 'subscription_created', {
309+
plan: subscriptionData.plan ?? 'unknown',
300310
status: subscriptionData.status,
311+
reference_id: subscriptionData.referenceId,
312+
})
313+
} catch (instrumentationError) {
314+
logger.warn('Failed to record subscription-created instrumentation', {
315+
subscriptionId: subscriptionData.id,
301316
referenceId: subscriptionData.referenceId,
302-
...(isOrgScoped ? { organizationId: subscriptionData.referenceId } : {}),
303-
},
304-
})
305-
captureServerEvent(subscriptionData.referenceId, 'subscription_created', {
306-
plan: subscriptionData.plan ?? 'unknown',
307-
status: subscriptionData.status,
308-
reference_id: subscriptionData.referenceId,
309-
})
317+
error: instrumentationError,
318+
})
319+
}
310320
}
311321
}
312322
)

0 commit comments

Comments
 (0)