refactor(helcim): wrapped PATCH body, first-activation welcome email guard
Moves updateHelcimSubscription to the live-verified wrapped shape
(PATCH /subscriptions { subscriptions: [{ id, ...payload }] }), adds a prior-
status check so sendWelcomeEmail only fires on pending_payment to active
transitions, short-circuits get-or-create-customer when a valid
helcimCustomerId is already on file, and replaces member.save() Slack-status
writes with findByIdAndUpdate({ runValidators: false }) to avoid save-time
validator pitfalls.
This commit is contained in:
parent
37a58cb0eb
commit
4f567e9586
4 changed files with 100 additions and 41 deletions
|
|
@ -5,6 +5,7 @@ import { connectDB } from '../../utils/mongoose.js'
|
|||
import { getSlackService } from '../../utils/slack.ts'
|
||||
import { requireAuth } from '../../utils/auth.js'
|
||||
import { createHelcimSubscription, generateIdempotencyKey } from '../../utils/helcim.js'
|
||||
import { sendWelcomeEmail } from '../../utils/resend.js'
|
||||
|
||||
// Function to invite member to Slack
|
||||
async function inviteToSlack(member) {
|
||||
|
|
@ -23,20 +24,23 @@ async function inviteToSlack(member) {
|
|||
)
|
||||
|
||||
if (inviteResult.success) {
|
||||
// Update member record based on the actual result
|
||||
if (inviteResult.status === 'existing_user_added_to_channel' ||
|
||||
inviteResult.status === 'user_already_in_channel' ||
|
||||
const update = {}
|
||||
if (inviteResult.status === 'existing_user_added_to_channel' ||
|
||||
inviteResult.status === 'user_already_in_channel' ||
|
||||
inviteResult.status === 'new_user_invited_to_workspace') {
|
||||
member.slackInviteStatus = 'sent'
|
||||
member.slackUserId = inviteResult.userId
|
||||
member.slackInvited = true
|
||||
update.slackInviteStatus = 'sent'
|
||||
update.slackUserId = inviteResult.userId
|
||||
update.slackInvited = true
|
||||
} else {
|
||||
// Manual invitation required
|
||||
member.slackInviteStatus = 'pending'
|
||||
member.slackInvited = false
|
||||
update.slackInviteStatus = 'pending'
|
||||
update.slackInvited = false
|
||||
}
|
||||
await member.save()
|
||||
|
||||
await Member.findByIdAndUpdate(
|
||||
member._id,
|
||||
{ $set: update },
|
||||
{ runValidators: false }
|
||||
)
|
||||
|
||||
// Send notification to vetting channel
|
||||
await slackService.notifyNewMember(
|
||||
member.name,
|
||||
|
|
@ -45,23 +49,27 @@ async function inviteToSlack(member) {
|
|||
member.contributionTier,
|
||||
inviteResult.status
|
||||
)
|
||||
|
||||
|
||||
console.log(`Successfully processed Slack invitation for ${member.email}: ${inviteResult.status}`)
|
||||
} else {
|
||||
// Update member record to reflect failed invitation
|
||||
member.slackInviteStatus = 'failed'
|
||||
await member.save()
|
||||
|
||||
await Member.findByIdAndUpdate(
|
||||
member._id,
|
||||
{ $set: { slackInviteStatus: 'failed' } },
|
||||
{ runValidators: false }
|
||||
)
|
||||
|
||||
console.error(`Failed to process Slack invitation for ${member.email}: ${inviteResult.error}`)
|
||||
// Don't throw error - subscription creation should still succeed
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error during Slack invitation process:', error)
|
||||
|
||||
// Update member record to reflect failed invitation
|
||||
|
||||
try {
|
||||
member.slackInviteStatus = 'failed'
|
||||
await member.save()
|
||||
await Member.findByIdAndUpdate(
|
||||
member._id,
|
||||
{ $set: { slackInviteStatus: 'failed' } },
|
||||
{ runValidators: false }
|
||||
)
|
||||
} catch (saveError) {
|
||||
console.error('Failed to update member Slack status:', saveError)
|
||||
}
|
||||
|
|
@ -76,6 +84,14 @@ export default defineEventHandler(async (event) => {
|
|||
await connectDB()
|
||||
const body = await validateBody(event, helcimSubscriptionSchema)
|
||||
|
||||
// Only send welcome email when a member transitions from pending_payment
|
||||
// to active for the first time — not on tier upgrades (active → active).
|
||||
const priorMember = await Member.findOne(
|
||||
{ helcimCustomerId: body.customerId },
|
||||
{ status: 1 }
|
||||
)
|
||||
const isFirstActivation = priorMember?.status === 'pending_payment'
|
||||
|
||||
// Check if payment is required
|
||||
if (!requiresPayment(body.contributionTier)) {
|
||||
// For free tier, just update member status
|
||||
|
|
@ -91,8 +107,8 @@ export default defineEventHandler(async (event) => {
|
|||
|
||||
logActivity(member._id, 'subscription_created', { tier: body.contributionTier })
|
||||
|
||||
// Send Slack invitation for free tier members
|
||||
await inviteToSlack(member)
|
||||
if (isFirstActivation) await sendWelcomeEmail(member)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
|
@ -134,8 +150,8 @@ export default defineEventHandler(async (event) => {
|
|||
{ new: true }
|
||||
)
|
||||
|
||||
// Send Slack invitation even when no plan is configured
|
||||
await inviteToSlack(member)
|
||||
if (isFirstActivation) await sendWelcomeEmail(member)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
|
@ -194,8 +210,8 @@ export default defineEventHandler(async (event) => {
|
|||
|
||||
logActivity(member._id, 'subscription_created', { tier: body.contributionTier })
|
||||
|
||||
// Send Slack invitation for paid tier members
|
||||
await inviteToSlack(member)
|
||||
if (isFirstActivation) await sendWelcomeEmail(member)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
|
@ -236,8 +252,8 @@ export default defineEventHandler(async (event) => {
|
|||
{ new: true }
|
||||
)
|
||||
|
||||
// Send Slack invitation even when subscription setup fails
|
||||
await inviteToSlack(member)
|
||||
if (isFirstActivation) await sendWelcomeEmail(member)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue