Skip to content

Commit

Permalink
chore(api): add more logs to polar integration
Browse files Browse the repository at this point in the history
  • Loading branch information
vernu committed Feb 15, 2025
1 parent f3e999e commit 8cdc2b8
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 11 deletions.
3 changes: 3 additions & 0 deletions api/src/billing/billing.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export class BillingController {
req.headers,
)

// store the payload in the database
await this.billingService.storePolarWebhookPayload(payload)

// Handle Polar.sh webhook events
switch (payload.type) {
case 'subscription.created':
Expand Down
2 changes: 2 additions & 0 deletions api/src/billing/billing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import { MongooseModule } from '@nestjs/mongoose'
import { AuthModule } from 'src/auth/auth.module'
import { UsersModule } from 'src/users/users.module'
import { GatewayModule } from 'src/gateway/gateway.module'
import { PolarWebhookPayload, PolarWebhookPayloadSchema } from './schemas/polar-webhook-payload.schema'

@Module({
imports: [
MongooseModule.forFeature([
{ name: Plan.name, schema: PlanSchema },
{ name: Subscription.name, schema: SubscriptionSchema },
{ name: PolarWebhookPayload.name, schema: PolarWebhookPayloadSchema },
]),
AuthModule,
UsersModule,
Expand Down
48 changes: 37 additions & 11 deletions api/src/billing/billing.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { HttpException, HttpStatus, Injectable } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { Model } from 'mongoose'
import { Model, Types } from 'mongoose'
import { Plan, PlanDocument } from './schemas/plan.schema'
import {
Subscription,
Expand All @@ -12,6 +12,7 @@ import { CheckoutResponseDTO, PlanDTO } from './billing.dto'
import { SMSDocument } from 'src/gateway/schemas/sms.schema'
import { SMS } from 'src/gateway/schemas/sms.schema'
import { validateEvent } from '@polar-sh/sdk/webhooks'
import { PolarWebhookPayload, PolarWebhookPayloadDocument } from './schemas/polar-webhook-payload.schema'

@Injectable()
export class BillingService {
Expand All @@ -23,6 +24,8 @@ export class BillingService {
private subscriptionModel: Model<SubscriptionDocument>,
@InjectModel(User.name) private userModel: Model<UserDocument>,
@InjectModel(SMS.name) private smsModel: Model<SMSDocument>,
@InjectModel(PolarWebhookPayload.name)
private polarWebhookPayloadModel: Model<PolarWebhookPayloadDocument>,
) {
this.initializePlans()
this.polarApi = new Polar({
Expand Down Expand Up @@ -228,11 +231,11 @@ export class BillingService {
newPlanName?: string
newPlanPolarProductId?: string
}) {
// switch the subscription to the new one
// deactivate the current active subscription
// activate the new subscription if it exists or create a new one
console.log(`Switching plan for user: ${userId}`);

// Convert userId to ObjectId
const userObjectId = new Types.ObjectId(userId);

// get the plan from the polarProductId
let plan: PlanDocument
if (newPlanPolarProductId) {
plan = await this.planModel.findOne({
Expand All @@ -246,18 +249,24 @@ export class BillingService {
throw new Error('Plan not found')
}

// if any of the subscriptions that are not the new plan are active, deactivate them
await this.subscriptionModel.updateMany(
{ user: userId, plan: { $ne: plan._id }, isActive: true },
console.log(`Found plan: ${plan.name}`);

// Deactivate current active subscriptions
const result = await this.subscriptionModel.updateMany(
{ user: userObjectId, plan: { $ne: plan._id }, isActive: true },
{ isActive: false, endDate: new Date() },
)
console.log(`Deactivated subscriptions: ${result.modifiedCount}`);

// create or update the new subscription
await this.subscriptionModel.updateOne(
{ user: userId, plan: plan._id },
// Create or update the new subscription
const updateResult = await this.subscriptionModel.updateOne(
{ user: userObjectId, plan: plan._id },
{ isActive: true },
{ upsert: true },
)
console.log(`Updated or created subscription: ${updateResult.upsertedCount > 0 ? 'Created' : 'Updated'}`);

return { success: true, plan: plan.name };
}

async canPerformAction(
Expand Down Expand Up @@ -413,7 +422,24 @@ export class BillingService {
)
return webhookPayload
} catch (error) {
console.log('failed to validate polar webhook payload')
console.error(error)
throw new Error('Invalid webhook payload')
}
}

async storePolarWebhookPayload(payload: any) {
const userId = payload.data?.metadata?.userId || payload.data?.userId
const eventType = payload.type
const name = payload.data?.customer?.name
const email = payload.data?.customer?.email

await this.polarWebhookPayloadModel.create({
userId,
eventType,
name,
email,
payload,
})
}
}
24 changes: 24 additions & 0 deletions api/src/billing/schemas/polar-webhook-payload.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
import { Document } from 'mongoose'

export type PolarWebhookPayloadDocument = PolarWebhookPayload & Document

@Schema({ timestamps: true })
export class PolarWebhookPayload {
@Prop()
userId: string

@Prop()
eventType: string

@Prop()
name: string

@Prop()
email: string

@Prop({ type: Object })
payload: Record<string, any>
}

export const PolarWebhookPayloadSchema = SchemaFactory.createForClass(PolarWebhookPayload)

0 comments on commit 8cdc2b8

Please sign in to comment.