(
request: FastifyRequest<{
Querystring: unknown;
}>,
reply: FastifyReply
)
| 314 | } |
| 315 | |
| 316 | export async function polarWebhook( |
| 317 | request: FastifyRequest<{ |
| 318 | Querystring: unknown; |
| 319 | }>, |
| 320 | reply: FastifyReply |
| 321 | ) { |
| 322 | request.log.info({ body: request.body }, 'polar webhook received'); |
| 323 | |
| 324 | const validation = await tryCatch(async () => |
| 325 | validatePolarEvent( |
| 326 | request.rawBody!, |
| 327 | request.headers as Record<string, string>, |
| 328 | process.env.POLAR_WEBHOOK_SECRET ?? '' |
| 329 | ) |
| 330 | ); |
| 331 | |
| 332 | if (!validation.ok) { |
| 333 | request.log.error( |
| 334 | { err: validation.error }, |
| 335 | 'polar webhook: failed to parse event' |
| 336 | ); |
| 337 | throw validation.error; |
| 338 | } |
| 339 | |
| 340 | const event = validation.data; |
| 341 | |
| 342 | const eventOrganizationId = |
| 343 | 'metadata' in event.data && |
| 344 | event.data.metadata && |
| 345 | typeof event.data.metadata === 'object' && |
| 346 | 'organizationId' in event.data.metadata |
| 347 | ? String(event.data.metadata.organizationId) |
| 348 | : undefined; |
| 349 | |
| 350 | const eventCtx = { |
| 351 | eventType: event.type, |
| 352 | eventId: 'id' in event.data ? event.data.id : undefined, |
| 353 | organizationId: eventOrganizationId, |
| 354 | }; |
| 355 | |
| 356 | request.log.info( |
| 357 | eventCtx, |
| 358 | `polar webhook: processing ${event.type}${eventOrganizationId ? ` for ${eventOrganizationId}` : ''}` |
| 359 | ); |
| 360 | |
| 361 | if ( |
| 362 | 'data' in event && |
| 363 | 'product' in event.data && |
| 364 | event.data.product?.name === 'Supporter' |
| 365 | ) { |
| 366 | request.log.info(eventCtx, 'polar webhook: supporter event ignored'); |
| 367 | return reply.status(202).send('OK'); |
| 368 | } |
| 369 | |
| 370 | const handler = await tryCatch(async () => { |
| 371 | switch (event.type) { |
| 372 | // A new paid billing cycle resets the org's usage counter. Polar sends |
| 373 | // `order.updated` (not `order.created`) and the order moves through |
nothing calls this directly
no test coverage detected