Skip to content

Commit 012a2d5

Browse files
authored
feat: webhook logging for better debugging (#135)
- Disable request logging - Log event type, event id and entity id for better debuggability
2 parents 358fad1 + 6df54c5 commit 012a2d5

File tree

6 files changed

+91
-37
lines changed

6 files changed

+91
-37
lines changed

docker/compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,3 @@ services:
1010
POSTGRES_PASSWORD: postgres
1111
volumes:
1212
- ./db/migrations/schema.sql:/docker-entrypoint-initdb.d/0000_schema.sql
13-
- ../db/migrations:/docker-entrypoint-initdb.d

src/logger.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import pino from 'pino'
2+
3+
export const logger = pino({
4+
formatters: {
5+
level(label) {
6+
return { level: label }
7+
},
8+
},
9+
timestamp: pino.stdTimeFunctions.isoTime,
10+
})

src/routes/webhooks.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { upsertPaymentIntents } from '../lib/payment_intents'
1616
import { upsertSubscriptionSchedules } from '../lib/subscription_schedules'
1717
import { deleteTaxId, upsertTaxIds } from '../lib/tax_ids'
1818
import { upsertCreditNotes } from '../lib/creditNotes'
19+
import { logger } from '../logger'
1920

2021
const config = getConfig()
2122

@@ -41,13 +42,19 @@ export default async function routes(fastify: FastifyInstance) {
4142
case 'charge.succeeded':
4243
case 'charge.updated': {
4344
const charge = event.data.object as Stripe.Charge
45+
46+
logger.info(`Received webhook ${event.id}: ${event.type} for charge ${charge.id}`)
47+
4448
await upsertCharges([charge])
4549
break
4650
}
4751
case 'customer.created':
4852
case 'customer.deleted':
4953
case 'customer.updated': {
5054
const customer = event.data.object as Stripe.Customer
55+
56+
logger.info(`Received webhook ${event.id}: ${event.type} for customer ${customer.id}`)
57+
5158
await upsertCustomers([customer])
5259
break
5360
}
@@ -60,17 +67,28 @@ export default async function routes(fastify: FastifyInstance) {
6067
case 'customer.subscription.resumed':
6168
case 'customer.subscription.updated': {
6269
const subscription = event.data.object as Stripe.Subscription
70+
71+
logger.info(
72+
`Received webhook ${event.id}: ${event.type} for subscription ${subscription.id}`
73+
)
74+
6375
await upsertSubscriptions([subscription])
6476
break
6577
}
6678
case 'customer.tax_id.updated':
6779
case 'customer.tax_id.created': {
6880
const taxId = event.data.object as Stripe.TaxId
81+
82+
logger.info(`Received webhook ${event.id}: ${event.type} for taxId ${taxId.id}`)
83+
6984
await upsertTaxIds([taxId])
7085
break
7186
}
7287
case 'customer.tax_id.deleted': {
7388
const taxId = event.data.object as Stripe.TaxId
89+
90+
logger.info(`Received webhook ${event.id}: ${event.type} for taxId ${taxId.id}`)
91+
7492
await deleteTaxId(taxId.id)
7593
break
7694
}
@@ -90,39 +108,60 @@ export default async function routes(fastify: FastifyInstance) {
90108
case 'invoice.marked_uncollectible':
91109
case 'invoice.updated': {
92110
const invoice = event.data.object as Stripe.Invoice
111+
112+
logger.info(`Received webhook ${event.id}: ${event.type} for invoice ${invoice.id}`)
113+
93114
await upsertInvoices([invoice])
94115
break
95116
}
96117
case 'product.created':
97118
case 'product.updated': {
98119
const product = event.data.object as Stripe.Product
120+
121+
logger.info(`Received webhook ${event.id}: ${event.type} for product ${product.id}`)
122+
99123
await upsertProducts([product])
100124
break
101125
}
102126
case 'product.deleted': {
103127
const product = event.data.object as Stripe.Product
128+
129+
logger.info(`Received webhook ${event.id}: ${event.type} for product ${product.id}`)
130+
104131
await deleteProduct(product.id)
105132
break
106133
}
107134
case 'price.created':
108135
case 'price.updated': {
109136
const price = event.data.object as Stripe.Price
137+
138+
logger.info(`Received webhook ${event.id}: ${event.type} for price ${price.id}`)
139+
110140
await upsertPrices([price])
111141
break
112142
}
113143
case 'price.deleted': {
114144
const price = event.data.object as Stripe.Price
145+
146+
logger.info(`Received webhook ${event.id}: ${event.type} for price ${price.id}`)
147+
115148
await deletePrice(price.id)
116149
break
117150
}
118151
case 'plan.created':
119152
case 'plan.updated': {
120153
const plan = event.data.object as Stripe.Plan
154+
155+
logger.info(`Received webhook ${event.id}: ${event.type} for plan ${plan.id}`)
156+
121157
await upsertPlans([plan])
122158
break
123159
}
124160
case 'plan.deleted': {
125161
const plan = event.data.object as Stripe.Plan
162+
163+
logger.info(`Received webhook ${event.id}: ${event.type} for plan ${plan.id}`)
164+
126165
await deletePlan(plan.id)
127166
break
128167
}
@@ -133,6 +172,10 @@ export default async function routes(fastify: FastifyInstance) {
133172
case 'setup_intent.succeeded': {
134173
const setupIntent = event.data.object as Stripe.SetupIntent
135174

175+
logger.info(
176+
`Received webhook ${event.id}: ${event.type} for setupIntent ${setupIntent.id}`
177+
)
178+
136179
await upsertSetupIntents([setupIntent])
137180
break
138181
}
@@ -145,6 +188,10 @@ export default async function routes(fastify: FastifyInstance) {
145188
case 'subscription_schedule.updated': {
146189
const subscriptionSchedule = event.data.object as Stripe.SubscriptionSchedule
147190

191+
logger.info(
192+
`Received webhook ${event.id}: ${event.type} for subscriptionSchedule ${subscriptionSchedule.id}`
193+
)
194+
148195
await upsertSubscriptionSchedules([subscriptionSchedule])
149196
break
150197
}
@@ -154,6 +201,10 @@ export default async function routes(fastify: FastifyInstance) {
154201
case 'payment_method.updated': {
155202
const paymentMethod = event.data.object as Stripe.PaymentMethod
156203

204+
logger.info(
205+
`Received webhook ${event.id}: ${event.type} for paymentMethod ${paymentMethod.id}`
206+
)
207+
157208
await upsertPaymentMethods([paymentMethod])
158209
break
159210
}
@@ -164,6 +215,8 @@ export default async function routes(fastify: FastifyInstance) {
164215
case 'charge.dispute.closed': {
165216
const dispute = event.data.object as Stripe.Dispute
166217

218+
logger.info(`Received webhook ${event.id}: ${event.type} for dispute ${dispute.id}`)
219+
167220
await upsertDisputes([dispute])
168221
break
169222
}
@@ -177,6 +230,10 @@ export default async function routes(fastify: FastifyInstance) {
177230
case 'payment_intent.succeeded': {
178231
const paymentIntent = event.data.object as Stripe.PaymentIntent
179232

233+
logger.info(
234+
`Received webhook ${event.id}: ${event.type} for paymentIntent ${paymentIntent.id}`
235+
)
236+
180237
await upsertPaymentIntents([paymentIntent])
181238
break
182239
}
@@ -186,6 +243,8 @@ export default async function routes(fastify: FastifyInstance) {
186243
case 'credit_note.voided': {
187244
const creditNote = event.data.object as Stripe.CreditNote
188245

246+
logger.info(`Received webhook ${event.id}: ${event.type} for creditNote ${creditNote.id}`)
247+
189248
await upsertCreditNotes([creditNote])
190249
break
191250
}

src/server.ts

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,52 +2,23 @@ import { FastifyInstance } from 'fastify'
22
import { Server, IncomingMessage, ServerResponse } from 'node:http'
33
import { runMigrations } from './utils/migrate'
44
import { createServer } from './app'
5-
import pino from 'pino'
65
import { getConfig } from './utils/config'
7-
import { Client } from 'pg'
6+
import { logger } from './logger'
87

98
const config = getConfig()
109

11-
const logger = pino({
12-
formatters: {
13-
level(label) {
14-
return { level: label }
15-
},
16-
},
17-
timestamp: pino.stdTimeFunctions.isoTime,
18-
})
19-
2010
const main = async () => {
2111
const app: FastifyInstance<Server, IncomingMessage, ServerResponse> = await createServer({
2212
loggerInstance: logger,
13+
disableRequestLogging: true,
2314
exposeDocs: getConfig().NODE_ENV !== 'production',
2415
requestIdHeader: 'Request-Id',
2516
})
2617

27-
// Init config
28-
const port = getConfig().PORT
29-
30-
// Init DB
31-
const dbConfig = {
32-
connectionString: config.DATABASE_URL,
33-
connectionTimeoutMillis: 10_000,
34-
}
35-
const client = new Client(dbConfig)
36-
37-
// Run migrations
38-
try {
39-
await client.connect()
40-
41-
// Ensure schema exists, not doing it via migration to not break current migration checksums
42-
await client.query(`CREATE SCHEMA IF NOT EXISTS ${config.SCHEMA};`)
43-
44-
await runMigrations(client)
45-
} finally {
46-
await client.end()
47-
}
18+
await runMigrations()
4819

4920
// Start the server
50-
app.listen({ port: Number(port), host: '0.0.0.0' }, (err, address) => {
21+
app.listen({ port: Number(config.PORT), host: '0.0.0.0' }, (err, address) => {
5122
if (err) {
5223
console.error(err)
5324
process.exit(1)

src/utils/migrate.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,26 @@ async function connectAndMigrate(client: Client, migrationsDirectory: string, lo
2727
}
2828
}
2929

30-
export async function runMigrations(client: Client): Promise<void> {
30+
export async function runMigrations(): Promise<void> {
31+
// Init DB
32+
const dbConfig = {
33+
connectionString: config.DATABASE_URL,
34+
connectionTimeoutMillis: 10_000,
35+
}
36+
const client = new Client(dbConfig)
37+
3138
try {
39+
// Run migrations
40+
await client.connect()
41+
42+
// Ensure schema exists, not doing it via migration to not break current migration checksums
43+
await client.query(`CREATE SCHEMA IF NOT EXISTS ${config.SCHEMA};`)
44+
3245
console.log('Running migrations')
46+
3347
await connectAndMigrate(client, './db/migrations')
34-
} catch (error) {
35-
throw error
3648
} finally {
49+
await client.end()
3750
console.log('Finished migrations')
3851
}
3952
}

test/webhooks.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { createServer } from '../src/app'
55
import { getConfig } from '../src/utils/config'
66
import stripeMock from './helpers/stripe'
77
import 'dotenv/config'
8+
import { runMigrations } from '../src/utils/migrate'
89

910
jest.doMock('stripe', () => {
1011
return jest.fn(() => stripeMock)
@@ -18,6 +19,7 @@ describe('/webhooks', () => {
1819

1920
beforeAll(async () => {
2021
server = await createServer()
22+
await runMigrations()
2123
})
2224

2325
afterAll(async () => {

0 commit comments

Comments
 (0)