فهرست منبع

Merge pull request #26566 from overleaf/rh-cio-event-segmentation

Include customer.io segmentation in recurly/stripe subscription events

GitOrigin-RevId: 54483e17eec5251ac7871d43a3aa1e074dcfe98d
roo hutton 1 سال پیش
والد
کامیت
2a9d3bb168

+ 2 - 6
services/web/app/src/Features/SplitTests/SplitTestHandler.js

@@ -145,16 +145,12 @@ async function hasUserBeenAssignedToVariant(
   ignoreVersion = false
   ignoreVersion = false
 ) {
 ) {
   try {
   try {
-    const { session, query = {} } = req
+    const { session = {}, query = {} } = req
 
 
     const splitTest = await _getSplitTest(splitTestName)
     const splitTest = await _getSplitTest(splitTestName)
     const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
     const currentVersion = SplitTestUtils.getCurrentVersion(splitTest)
 
 
-    if (
-      !userId ||
-      !SessionManager.isUserLoggedIn(session) ||
-      !currentVersion?.active
-    ) {
+    if (!userId || !currentVersion?.active) {
       return false
       return false
     }
     }
 
 

+ 18 - 0
services/web/app/src/Features/Subscription/RecurlyEventHandler.js

@@ -12,6 +12,16 @@ async function sendRecurlyAnalyticsEvent(event, eventData) {
     return
     return
   }
   }
 
 
+  const customerIoEnabled =
+    await SplitTestHandler.promises.hasUserBeenAssignedToVariant(
+      {},
+      userId,
+      'customer-io-trial-conversion',
+      'enabled',
+      true
+    )
+  eventData['customerio-integration'] = customerIoEnabled || false
+
   switch (event) {
   switch (event) {
     case 'new_subscription_notification':
     case 'new_subscription_notification':
       await _sendSubscriptionStartedEvent(userId, eventData)
       await _sendSubscriptionStartedEvent(userId, eventData)
@@ -67,6 +77,7 @@ async function _sendSubscriptionResumedEvent(userId, eventData) {
       plan_code: planCode,
       plan_code: planCode,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(
@@ -89,6 +100,7 @@ async function _sendSubscriptionPausedEvent(userId, eventData) {
       plan_code: planCode,
       plan_code: planCode,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(
@@ -111,6 +123,7 @@ async function _sendSubscriptionStartedEvent(userId, eventData) {
       has_ai_add_on: hasAiAddOn,
       has_ai_add_on: hasAiAddOn,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(
@@ -158,6 +171,7 @@ async function _sendSubscriptionUpdatedEvent(userId, eventData) {
       has_ai_add_on: hasAiAddOn,
       has_ai_add_on: hasAiAddOn,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(
@@ -190,6 +204,7 @@ async function _sendSubscriptionCancelledEvent(userId, eventData) {
       has_ai_add_on: hasAiAddOn,
       has_ai_add_on: hasAiAddOn,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(
@@ -217,6 +232,7 @@ async function _sendSubscriptionExpiredEvent(userId, eventData) {
       has_ai_add_on: hasAiAddOn,
       has_ai_add_on: hasAiAddOn,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(
@@ -249,6 +265,7 @@ async function _sendSubscriptionRenewedEvent(userId, eventData) {
       has_ai_add_on: hasAiAddOn,
       has_ai_add_on: hasAiAddOn,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(
@@ -280,6 +297,7 @@ async function _sendSubscriptionReactivatedEvent(userId, eventData) {
       has_ai_add_on: hasAiAddOn,
       has_ai_add_on: hasAiAddOn,
       subscriptionId,
       subscriptionId,
       payment_provider: 'recurly',
       payment_provider: 'recurly',
+      'customerio-integration': eventData['customerio-integration'],
     }
     }
   )
   )
   AnalyticsManager.setUserPropertyForUserInBackground(
   AnalyticsManager.setUserPropertyForUserInBackground(

+ 110 - 18
services/web/test/unit/src/Subscription/RecurlyEventHandlerTests.js

@@ -41,14 +41,56 @@ describe('RecurlyEventHandler', function () {
             getAssignmentForUser: sinon.stub().resolves({
             getAssignmentForUser: sinon.stub().resolves({
               variant: 'default',
               variant: 'default',
             }),
             }),
+            hasUserBeenAssignedToVariant: sinon.stub().resolves(false),
           },
           },
         }),
         }),
       },
       },
     })
     })
   })
   })
 
 
-  it('with new_subscription_notification - free trial', function () {
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+  it('with new_subscription_notification - free trial', async function () {
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+      'new_subscription_notification',
+      this.eventData
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.recordEventForUserInBackground,
+      this.userId,
+      'subscription-started',
+      {
+        plan_code: this.planCode,
+        quantity: 1,
+        is_trial: true,
+        has_ai_add_on: false,
+        subscriptionId: this.eventData.subscription.uuid,
+        payment_provider: 'recurly',
+        'customerio-integration': false,
+      }
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.setUserPropertyForUserInBackground,
+      this.userId,
+      'subscription-plan-code',
+      this.planCode
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.setUserPropertyForUserInBackground,
+      this.userId,
+      'subscription-state',
+      'active'
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.setUserPropertyForUserInBackground,
+      this.userId,
+      'subscription-is-trial',
+      true
+    )
+  })
+
+  it('with new_subscription_notification - free trial with customerio integration enabled', async function () {
+    this.SplitTestHandler.promises.hasUserBeenAssignedToVariant.resolves(true)
+
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'new_subscription_notification',
       'new_subscription_notification',
       this.eventData
       this.eventData
     )
     )
@@ -63,6 +105,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: this.eventData.subscription.uuid,
         subscriptionId: this.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
+        'customerio-integration': true,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -94,7 +137,7 @@ describe('RecurlyEventHandler', function () {
     sinon.assert.called(this.SubscriptionEmailHandler.sendTrialOnboardingEmail)
     sinon.assert.called(this.SubscriptionEmailHandler.sendTrialOnboardingEmail)
   })
   })
 
 
-  it('with new_subscription_notification - no free trial', function () {
+  it('with new_subscription_notification - no free trial', async function () {
     this.eventData.subscription.current_period_started_at = new Date(
     this.eventData.subscription.current_period_started_at = new Date(
       '2021-02-10 12:34:56'
       '2021-02-10 12:34:56'
     )
     )
@@ -103,7 +146,7 @@ describe('RecurlyEventHandler', function () {
     )
     )
     this.eventData.subscription.quantity = 3
     this.eventData.subscription.quantity = 3
 
 
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'new_subscription_notification',
       'new_subscription_notification',
       this.eventData
       this.eventData
     )
     )
@@ -118,6 +161,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: this.eventData.subscription.uuid,
         subscriptionId: this.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
+        'customerio-integration': false,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -134,10 +178,53 @@ describe('RecurlyEventHandler', function () {
     )
     )
   })
   })
 
 
-  it('with updated_subscription_notification', function () {
+  it('with updated_subscription_notification', async function () {
     this.planCode = 'new-plan-code'
     this.planCode = 'new-plan-code'
     this.eventData.subscription.plan.plan_code = this.planCode
     this.eventData.subscription.plan.plan_code = this.planCode
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+      'updated_subscription_notification',
+      this.eventData
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.recordEventForUserInBackground,
+      this.userId,
+      'subscription-updated',
+      {
+        plan_code: this.planCode,
+        quantity: 1,
+        is_trial: true,
+        has_ai_add_on: false,
+        subscriptionId: this.eventData.subscription.uuid,
+        payment_provider: 'recurly',
+        'customerio-integration': false,
+      }
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.setUserPropertyForUserInBackground,
+      this.userId,
+      'subscription-plan-code',
+      this.planCode
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.setUserPropertyForUserInBackground,
+      this.userId,
+      'subscription-state',
+      'active'
+    )
+    sinon.assert.calledWith(
+      this.AnalyticsManager.setUserPropertyForUserInBackground,
+      this.userId,
+      'subscription-is-trial',
+      true
+    )
+  })
+
+  it('with updated_subscription_notification with customerio integration enabled', async function () {
+    this.SplitTestHandler.promises.hasUserBeenAssignedToVariant.resolves(true)
+    this.planCode = 'new-plan-code'
+    this.eventData.subscription.plan.plan_code = this.planCode
+
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'updated_subscription_notification',
       'updated_subscription_notification',
       this.eventData
       this.eventData
     )
     )
@@ -152,6 +239,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: this.eventData.subscription.uuid,
         subscriptionId: this.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
+        'customerio-integration': true,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -191,6 +279,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: this.eventData.subscription.uuid,
         subscriptionId: this.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
+        'customerio-integration': false,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -207,9 +296,9 @@ describe('RecurlyEventHandler', function () {
     )
     )
   })
   })
 
 
-  it('with expired_subscription_notification', function () {
+  it('with expired_subscription_notification', async function () {
     this.eventData.subscription.state = 'expired'
     this.eventData.subscription.state = 'expired'
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'expired_subscription_notification',
       'expired_subscription_notification',
       this.eventData
       this.eventData
     )
     )
@@ -224,6 +313,7 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: this.eventData.subscription.uuid,
         subscriptionId: this.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
+        'customerio-integration': false,
       }
       }
     )
     )
     sinon.assert.calledWith(
     sinon.assert.calledWith(
@@ -246,8 +336,8 @@ describe('RecurlyEventHandler', function () {
     )
     )
   })
   })
 
 
-  it('with renewed_subscription_notification', function () {
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+  it('with renewed_subscription_notification', async function () {
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'renewed_subscription_notification',
       'renewed_subscription_notification',
       this.eventData
       this.eventData
     )
     )
@@ -262,12 +352,13 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: this.eventData.subscription.uuid,
         subscriptionId: this.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
+        'customerio-integration': false,
       }
       }
     )
     )
   })
   })
 
 
-  it('with reactivated_account_notification', function () {
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+  it('with reactivated_account_notification', async function () {
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'reactivated_account_notification',
       'reactivated_account_notification',
       this.eventData
       this.eventData
     )
     )
@@ -281,11 +372,12 @@ describe('RecurlyEventHandler', function () {
         has_ai_add_on: false,
         has_ai_add_on: false,
         subscriptionId: this.eventData.subscription.uuid,
         subscriptionId: this.eventData.subscription.uuid,
         payment_provider: 'recurly',
         payment_provider: 'recurly',
+        'customerio-integration': false,
       }
       }
     )
     )
   })
   })
 
 
-  it('with paid_charge_invoice_notification', function () {
+  it('with paid_charge_invoice_notification', async function () {
     const invoice = {
     const invoice = {
       invoice_number: 1234,
       invoice_number: 1234,
       currency: 'USD',
       currency: 'USD',
@@ -298,7 +390,7 @@ describe('RecurlyEventHandler', function () {
       collection_method: 'automatic',
       collection_method: 'automatic',
       subscription_ids: ['abcd1234', 'defa3214'],
       subscription_ids: ['abcd1234', 'defa3214'],
     }
     }
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'paid_charge_invoice_notification',
       'paid_charge_invoice_notification',
       {
       {
         account: {
         account: {
@@ -325,8 +417,8 @@ describe('RecurlyEventHandler', function () {
     )
     )
   })
   })
 
 
-  it('with paid_charge_invoice_notification and total_in_cents 0', function () {
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+  it('with paid_charge_invoice_notification and total_in_cents 0', async function () {
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'paid_charge_invoice_notification',
       'paid_charge_invoice_notification',
       {
       {
         account: {
         account: {
@@ -341,8 +433,8 @@ describe('RecurlyEventHandler', function () {
     sinon.assert.notCalled(this.AnalyticsManager.recordEventForUserInBackground)
     sinon.assert.notCalled(this.AnalyticsManager.recordEventForUserInBackground)
   })
   })
 
 
-  it('with closed_invoice_notification', function () {
-    this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
+  it('with closed_invoice_notification', async function () {
+    await this.RecurlyEventHandler.sendRecurlyAnalyticsEvent(
       'closed_invoice_notification',
       'closed_invoice_notification',
       {
       {
         account: {
         account: {