From d7b0cb908a18b2ff35baa871b15fa9fda070a7e7 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 4 Jun 2026 13:17:58 +0500 Subject: [PATCH 1/4] feat(condo): DOMA-13327 added webhook after subscription activation --- .../ActivateSubscriptionContextService.js | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js index bc4822bf800..a3f2eb88271 100644 --- a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js +++ b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js @@ -2,9 +2,11 @@ * Generated by `createservice subscription.ActivateSubscriptionContextService --type mutations` */ +const conf = require('@open-condo/config') const { GQLError, GQLErrorCode: { BAD_USER_INPUT } } = require('@open-condo/keystone/errors') const { getLogger } = require('@open-condo/keystone/logging') const { GQLCustomSchema, find, getById, itemsQuery } = require('@open-condo/keystone/schema') +const { queueWebhookPayload } = require('@open-condo/webhooks/utils/queueWebhookPayload') const { PAYMENT_DONE_STATUS, @@ -170,6 +172,38 @@ const ActivateSubscriptionContextService = new GQLCustomSchema('ActivateSubscrip frozenPaymentInfo, }) + if (conf['N8N_SUBSCRIPTION_WEBHOOK_URL'] && conf['N8N_SUBSCRIPTION_WEBHOOK_SECRET']) { + try { + const payerOrg = await getById('Organization', invoice.payerOrganization) + const createdByUser = await getById('User', invoice.createdBy) + await queueWebhookPayload(context, { + url: conf['N8N_SUBSCRIPTION_WEBHOOK_URL'], + secret: conf['N8N_SUBSCRIPTION_WEBHOOK_SECRET'], + eventType: 'subscription.activated', + modelName: 'SubscriptionContext', + itemId: subscriptionContext.id, + payload: { + invoiceId: invoice.id, + paidAt: invoice.paidAt, + toPay: invoice.toPay, + planName: invoice.rows?.[0]?.name, + organization: { + id: payerOrg?.id, + name: payerOrg?.name, + tin: payerOrg?.tin, + }, + user: { + id: createdByUser?.id, + name: createdByUser?.name, + }, + }, + sender: { dv: 1, fingerprint: 'activateSubscriptionContext' }, + }) + } catch (err) { + logger.error({ msg: 'Failed to queue subscription webhook', err, data: { subscriptionContextId: subscriptionContext.id } }) + } + } + const updatedSubscriptionContext = await getById('SubscriptionContext', subscriptionContext.id) logger.info({ msg: 'Subscription context activated successfully', data: { subscriptionContextId: subscriptionContext.id, status: updatedSubscriptionContext.status, bindingId: updatedSubscriptionContext.bindingId } }) From 6cca3ec802697aefc92477e25410b250bb05d1f2 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 4 Jun 2026 13:29:16 +0500 Subject: [PATCH 2/4] feat(condo): DOMA-13327 fix env names --- .helm | 2 +- .../schema/ActivateSubscriptionContextService.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.helm b/.helm index 3deb512e412..3f3dd3b2ec6 160000 --- a/.helm +++ b/.helm @@ -1 +1 @@ -Subproject commit 3deb512e412a8e3ee12903870a34a7b0751139dc +Subproject commit 3f3dd3b2ec6529a5fcc76a97b75161b1adc63774 diff --git a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js index a3f2eb88271..09aec2663da 100644 --- a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js +++ b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js @@ -172,13 +172,13 @@ const ActivateSubscriptionContextService = new GQLCustomSchema('ActivateSubscrip frozenPaymentInfo, }) - if (conf['N8N_SUBSCRIPTION_WEBHOOK_URL'] && conf['N8N_SUBSCRIPTION_WEBHOOK_SECRET']) { + if (conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_URL'] && conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET']) { try { const payerOrg = await getById('Organization', invoice.payerOrganization) const createdByUser = await getById('User', invoice.createdBy) await queueWebhookPayload(context, { - url: conf['N8N_SUBSCRIPTION_WEBHOOK_URL'], - secret: conf['N8N_SUBSCRIPTION_WEBHOOK_SECRET'], + url: conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_URL'], + secret: conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET'], eventType: 'subscription.activated', modelName: 'SubscriptionContext', itemId: subscriptionContext.id, From e01030fa5a77935117079ad6d0fa2227a3b8a893 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 4 Jun 2026 14:14:23 +0500 Subject: [PATCH 3/4] feat(condo): DOMA-13327 fixes after review --- .../domains/common/constants/webhooks.js | 3 ++ .../ActivateSubscriptionContextService.js | 33 ++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apps/condo/domains/common/constants/webhooks.js b/apps/condo/domains/common/constants/webhooks.js index 366e7333790..b84aea8f4e7 100644 --- a/apps/condo/domains/common/constants/webhooks.js +++ b/apps/condo/domains/common/constants/webhooks.js @@ -1,10 +1,13 @@ const WEBHOOK_EVENT_PAYMENT_STATUS_UPDATED = 'payment.status.updated' +const WEBHOOK_EVENT_SUBSCRIPTION_ACTIVATED = 'subscription.activated' const WEBHOOK_EVENTS = [ WEBHOOK_EVENT_PAYMENT_STATUS_UPDATED, + WEBHOOK_EVENT_SUBSCRIPTION_ACTIVATED, ] module.exports = { WEBHOOK_EVENT_PAYMENT_STATUS_UPDATED, + WEBHOOK_EVENT_SUBSCRIPTION_ACTIVATED, WEBHOOK_EVENTS, } diff --git a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js index 09aec2663da..1c000d22f6a 100644 --- a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js +++ b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js @@ -5,13 +5,14 @@ const conf = require('@open-condo/config') const { GQLError, GQLErrorCode: { BAD_USER_INPUT } } = require('@open-condo/keystone/errors') const { getLogger } = require('@open-condo/keystone/logging') -const { GQLCustomSchema, find, getById, itemsQuery } = require('@open-condo/keystone/schema') +const { GQLCustomSchema, find, getById, getSchemaCtx, itemsQuery } = require('@open-condo/keystone/schema') const { queueWebhookPayload } = require('@open-condo/webhooks/utils/queueWebhookPayload') const { PAYMENT_DONE_STATUS, } = require('@condo/domains/acquiring/constants/payment') const { freezePaymentInfo } = require('@condo/domains/acquiring/utils/billingFridge') +const { WEBHOOK_EVENT_SUBSCRIPTION_ACTIVATED } = require('@condo/domains/common/constants/webhooks') const { INVOICE_STATUS_PAID } = require('@condo/domains/marketplace/constants') const access = require('@condo/domains/subscription/access/ActivateSubscriptionContextService') const { SUBSCRIPTION_CONTEXT_STATUS } = require('@condo/domains/subscription/constants') @@ -19,6 +20,9 @@ const { SubscriptionContext } = require('@condo/domains/subscription/utils/serve const logger = getLogger('ActivateSubscriptionContextService') +const SUBSCRIPTION_ACTIVATED_WEBHOOK_URL = conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_URL'] +const SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET = conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET'] + /** * List of possible errors, that this custom schema can throw * They will be rendered in documentation section in GraphiQL for this custom schema @@ -172,14 +176,21 @@ const ActivateSubscriptionContextService = new GQLCustomSchema('ActivateSubscrip frozenPaymentInfo, }) - if (conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_URL'] && conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET']) { + if (SUBSCRIPTION_ACTIVATED_WEBHOOK_URL && SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET) { try { + const { keystone: internalContext } = getSchemaCtx('WebhookPayload') const payerOrg = await getById('Organization', invoice.payerOrganization) const createdByUser = await getById('User', invoice.createdBy) - await queueWebhookPayload(context, { - url: conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_URL'], - secret: conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET'], - eventType: 'subscription.activated', + if (!payerOrg) { + throw new Error(`Organization not found: ${invoice.payerOrganization}`) + } + if (!createdByUser) { + throw new Error(`User not found: ${invoice.createdBy}`) + } + await queueWebhookPayload(internalContext, { + url: SUBSCRIPTION_ACTIVATED_WEBHOOK_URL, + secret: SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET, + eventType: WEBHOOK_EVENT_SUBSCRIPTION_ACTIVATED, modelName: 'SubscriptionContext', itemId: subscriptionContext.id, payload: { @@ -188,13 +199,13 @@ const ActivateSubscriptionContextService = new GQLCustomSchema('ActivateSubscrip toPay: invoice.toPay, planName: invoice.rows?.[0]?.name, organization: { - id: payerOrg?.id, - name: payerOrg?.name, - tin: payerOrg?.tin, + id: payerOrg.id, + name: payerOrg.name, + tin: payerOrg.tin, }, user: { - id: createdByUser?.id, - name: createdByUser?.name, + id: createdByUser.id, + name: createdByUser.name, }, }, sender: { dv: 1, fingerprint: 'activateSubscriptionContext' }, From eebe6607cf061e937dae51634be9a0d770a8d2c7 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 4 Jun 2026 14:16:11 +0500 Subject: [PATCH 4/4] feat(condo): DOMA-13327 make types feat(condo): DOMA-13327 added tests feat(condo): DOMA-13327 added tests feat(condo): DOMA-13327 added tests --- .../ActivateSubscriptionContextService.js | 28 +++++++++---------- apps/condo/schema.graphql | 2 +- apps/condo/schema.ts | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js index 1c000d22f6a..d90f5915a15 100644 --- a/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js +++ b/apps/condo/domains/subscription/schema/ActivateSubscriptionContextService.js @@ -20,9 +20,6 @@ const { SubscriptionContext } = require('@condo/domains/subscription/utils/serve const logger = getLogger('ActivateSubscriptionContextService') -const SUBSCRIPTION_ACTIVATED_WEBHOOK_URL = conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_URL'] -const SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET = conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET'] - /** * List of possible errors, that this custom schema can throw * They will be rendered in documentation section in GraphiQL for this custom schema @@ -176,20 +173,21 @@ const ActivateSubscriptionContextService = new GQLCustomSchema('ActivateSubscrip frozenPaymentInfo, }) - if (SUBSCRIPTION_ACTIVATED_WEBHOOK_URL && SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET) { + const webhookUrl = conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_URL'] + const webhookSecret = conf['SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET'] + if (webhookUrl && webhookSecret) { try { const { keystone: internalContext } = getSchemaCtx('WebhookPayload') const payerOrg = await getById('Organization', invoice.payerOrganization) - const createdByUser = await getById('User', invoice.createdBy) if (!payerOrg) { throw new Error(`Organization not found: ${invoice.payerOrganization}`) } - if (!createdByUser) { - throw new Error(`User not found: ${invoice.createdBy}`) - } + const createdByUser = invoice.createdBy + ? await getById('User', invoice.createdBy) + : null await queueWebhookPayload(internalContext, { - url: SUBSCRIPTION_ACTIVATED_WEBHOOK_URL, - secret: SUBSCRIPTION_ACTIVATED_WEBHOOK_SECRET, + url: webhookUrl, + secret: webhookSecret, eventType: WEBHOOK_EVENT_SUBSCRIPTION_ACTIVATED, modelName: 'SubscriptionContext', itemId: subscriptionContext.id, @@ -203,10 +201,12 @@ const ActivateSubscriptionContextService = new GQLCustomSchema('ActivateSubscrip name: payerOrg.name, tin: payerOrg.tin, }, - user: { - id: createdByUser.id, - name: createdByUser.name, - }, + ...(createdByUser && { + user: { + id: createdByUser.id, + name: createdByUser.name, + }, + }), }, sender: { dv: 1, fingerprint: 'activateSubscriptionContext' }, }) diff --git a/apps/condo/schema.graphql b/apps/condo/schema.graphql index f6cb4c53019..85274d18322 100644 --- a/apps/condo/schema.graphql +++ b/apps/condo/schema.graphql @@ -91911,7 +91911,7 @@ type WebhookPayload { """ secret: String - """ Type of event that triggered this webhook. Custom events: payment.status.updated. Auto-generated events from models with webHooked() plugin: ModelName.created, ModelName.updated, ModelName.deleted (e.g., Payment.created, Ticket.updated). + """ Type of event that triggered this webhook. Custom events: payment.status.updated, subscription.activated. Auto-generated events from models with webHooked() plugin: ModelName.created, ModelName.updated, ModelName.deleted (e.g., Payment.created, Ticket.updated). """ eventType: String diff --git a/apps/condo/schema.ts b/apps/condo/schema.ts index f7f10631351..25dcd43cbfe 100644 --- a/apps/condo/schema.ts +++ b/apps/condo/schema.ts @@ -120089,7 +120089,7 @@ export type WebhookPayload = { deletedAt?: Maybe; /** Data structure Version */ dv?: Maybe; - /** Type of event that triggered this webhook. Custom events: payment.status.updated. Auto-generated events from models with webHooked() plugin: ModelName.created, ModelName.updated, ModelName.deleted (e.g., Payment.created, Ticket.updated). */ + /** Type of event that triggered this webhook. Custom events: payment.status.updated, subscription.activated. Auto-generated events from models with webHooked() plugin: ModelName.created, ModelName.updated, ModelName.deleted (e.g., Payment.created, Ticket.updated). */ eventType?: Maybe; /** Timestamp after which no more send attempts will be made */ expiresAt?: Maybe;