From 2e924f277a7b76a3dd5fb08e8aa75a387e868a40 Mon Sep 17 00:00:00 2001 From: isra el Date: Sat, 15 Feb 2025 18:34:13 +0300 Subject: [PATCH] chore(api): add more logs to polar integration --- api/src/billing/billing.controller.ts | 3 ++ api/src/billing/billing.module.ts | 2 + api/src/billing/billing.service.ts | 51 +++++++++++++++---- .../schemas/polar-webhook-payload.schema.ts | 24 +++++++++ 4 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 api/src/billing/schemas/polar-webhook-payload.schema.ts diff --git a/api/src/billing/billing.controller.ts b/api/src/billing/billing.controller.ts index e580c90..bbf4910 100644 --- a/api/src/billing/billing.controller.ts +++ b/api/src/billing/billing.controller.ts @@ -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': diff --git a/api/src/billing/billing.module.ts b/api/src/billing/billing.module.ts index 0b37d28..edd2f5f 100644 --- a/api/src/billing/billing.module.ts +++ b/api/src/billing/billing.module.ts @@ -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, diff --git a/api/src/billing/billing.service.ts b/api/src/billing/billing.service.ts index 91ce65d..32f2044 100644 --- a/api/src/billing/billing.service.ts +++ b/api/src/billing/billing.service.ts @@ -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, @@ -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 { @@ -23,6 +24,8 @@ export class BillingService { private subscriptionModel: Model, @InjectModel(User.name) private userModel: Model, @InjectModel(SMS.name) private smsModel: Model, + @InjectModel(PolarWebhookPayload.name) + private polarWebhookPayloadModel: Model, ) { this.initializePlans() this.polarApi = new Polar({ @@ -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({ @@ -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( @@ -405,6 +414,9 @@ export class BillingService { 'webhook-signature': headers['webhook-signature'] ?? '', } + console.log('webhookHeaders') + console.log(webhookHeaders) + try { const webhookPayload = validateEvent( payload, @@ -413,7 +425,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, + }) + } } diff --git a/api/src/billing/schemas/polar-webhook-payload.schema.ts b/api/src/billing/schemas/polar-webhook-payload.schema.ts new file mode 100644 index 0000000..16ff35b --- /dev/null +++ b/api/src/billing/schemas/polar-webhook-payload.schema.ts @@ -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 +} + +export const PolarWebhookPayloadSchema = SchemaFactory.createForClass(PolarWebhookPayload)