40 lines
1.1 KiB
TypeScript
40 lines
1.1 KiB
TypeScript
|
|
import { Router, Request, Response } from 'express'
|
||
|
|
import { syncEntitlement } from '../services/entitlement'
|
||
|
|
import { RevenueCatEvent } from '../types'
|
||
|
|
|
||
|
|
const router = Router()
|
||
|
|
|
||
|
|
function verifyRevenueCatSecret(req: Request): boolean {
|
||
|
|
const secret = process.env.REVENUECAT_WEBHOOK_SECRET
|
||
|
|
if (!secret) {
|
||
|
|
console.warn('[webhook] REVENUECAT_WEBHOOK_SECRET not set — skipping auth check')
|
||
|
|
return true
|
||
|
|
}
|
||
|
|
const auth = req.headers['authorization'] ?? ''
|
||
|
|
return auth === secret
|
||
|
|
}
|
||
|
|
|
||
|
|
router.post('/revenuecat', async (req: Request, res: Response) => {
|
||
|
|
if (!verifyRevenueCatSecret(req)) {
|
||
|
|
res.status(401).json({ error: 'unauthorized' })
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const body = req.body as RevenueCatEvent
|
||
|
|
if (!body?.event?.type || !body?.event?.app_user_id) {
|
||
|
|
res.status(400).json({ error: 'malformed payload' })
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// Acknowledge immediately — RC retries if we don't respond within 10s
|
||
|
|
res.status(200).json({ received: true })
|
||
|
|
|
||
|
|
try {
|
||
|
|
await syncEntitlement(body)
|
||
|
|
} catch (err) {
|
||
|
|
console.error('[webhook] entitlement sync failed:', err)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
export default router
|