Conversation
WalkthroughThe PR removes the default Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Ember E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20050501701 -n playwright-report-ember -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
1 similar comment
Ember E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20050501701 -n playwright-report-ember -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
Ember E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20077702586 -n playwright-report-ember -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
1 similar comment
Ember E2E Tests FailedTo view the Playwright test report locally, run: REPORT_DIR=$(mktemp -d) && gh run download 20077702586 -n playwright-report-ember -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR" |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js (1)
347-389: Free welcome email gating onstripeCustomermatches intentThe updated conditions on outbox creation and
StartOutboxProcessingEvent:
- Only run when the free welcome template is active, and
- Skip entirely when
stripeCustomeris present,are a good fit for avoiding free welcome emails on paid signups while preserving the free path behavior.
You might slightly tighten the coupling to persisted state by deriving the payload status instead of hardcoding it:
- payload: JSON.stringify({ + payload: JSON.stringify({ memberId: newMember.id, email: newMember.get('email'), name: newMember.get('name'), source, - timestamp, - memberStatus: 'free' + timestamp, + memberStatus: newMember.get('status') })This keeps
memberStatusin the payload consistent with whatever status we actually wrote to the member record (currently still'free'for this path).ghost/core/test/integration/jobs/process-outbox.test.js (1)
79-239: Outbox job fixtures correctly updated withmemberStatusUpdating all
MemberCreatedEventtest payloads to includememberStatus: 'free'(and ensuringnameis present) keeps these integration tests in sync with the new handler and service signature.Optionally, you could add a simple assertion on
memberStatusin one of the tests that inspects payloads (similar to the member‑welcome integration tests) to catch any future regressions, but the current coverage is already functionally sound.ghost/core/test/integration/services/member-welcome-emails.test.js (1)
71-82: Integration coverage formemberStatusin welcome email flow looks goodAsserting
payload.memberStatus === 'free'on member creation and addingmemberStatus: 'free'to the outbox entries used in the inactive/no‑template scenarios ensures the integration path matches the updated service contract and handler behavior.If you want slightly stronger guarantees, you could also assert
memberStatusin the inactive/no‑template tests when inspecting the stored payload, but it’s not strictly necessary given the existing checks.Also applies to: 191-203, 219-226
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
ghost/core/core/server/services/member-welcome-emails/service.js(1 hunks)ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js(3 hunks)ghost/core/core/server/services/outbox/handlers/member-created.js(1 hunks)ghost/core/test/integration/jobs/process-outbox.test.js(8 hunks)ghost/core/test/integration/services/member-welcome-emails.test.js(3 hunks)ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js(1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 25288
File: ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js:46-64
Timestamp: 2025-11-10T23:10:17.470Z
Learning: In ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js and the outbox processing flow, entries are marked as PROCESSING before being processed. If a failure occurs after email send but before deletion, the entry remains stuck in PROCESSING state (not reprocessed). This intentional design prevents duplicate emails. Handling stuck PROCESSING entries is planned for a separate PR.
📚 Learning: 2025-11-10T23:10:17.470Z
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 25288
File: ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js:46-64
Timestamp: 2025-11-10T23:10:17.470Z
Learning: In ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js and the outbox processing flow, entries are marked as PROCESSING before being processed. If a failure occurs after email send but before deletion, the entry remains stuck in PROCESSING state (not reprocessed). This intentional design prevents duplicate emails. Handling stuck PROCESSING entries is planned for a separate PR.
Applied to files:
ghost/core/test/integration/jobs/process-outbox.test.jsghost/core/core/server/services/member-welcome-emails/service.jsghost/core/test/integration/services/member-welcome-emails.test.jsghost/core/core/server/services/members/members-api/repositories/MemberRepository.jsghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.jsghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Applied to files:
ghost/core/core/server/services/member-welcome-emails/service.jsghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
ghost/core/test/integration/services/member-welcome-emails.test.js
🧬 Code graph analysis (3)
ghost/core/test/integration/services/member-welcome-emails.test.js (3)
ghost/core/test/integration/jobs/process-outbox.test.js (1)
assert(2-2)ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js (3)
assert(2-2)payload(558-558)payload(624-624)ghost/core/core/server/services/outbox/jobs/lib/process-entries.js (1)
payload(51-51)
ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js (2)
ghost/core/core/server/services/member-welcome-emails/service.js (1)
config(5-5)ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js (1)
config(11-11)
ghost/core/core/server/services/outbox/handlers/member-created.js (3)
ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js (1)
memberWelcomeEmailService(7-7)ghost/core/test/integration/services/member-welcome-emails.test.js (2)
payload(76-76)payload(147-147)ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js (2)
payload(558-558)payload(624-624)
🔇 Additional comments (2)
ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js (1)
673-715: Good regression test for paid signup path skipping free welcome emailThe new test that exercises
createwith astripeCustomerpayload and asserts thatOutbox.addis not called directly guards the main regression risk this PR addresses, while avoiding unrelated Stripe wiring via stubs onlinkSubscription/upsertCustomer. This is a solid addition to the suite.ghost/core/core/server/services/member-welcome-emails/service.js (1)
44-92: MakingmemberStatusexplicit insendis appropriateRequiring
memberStatusinsend({member, memberStatus})aligns with the new split of templates by member status and prevents accidental reliance on a'free'default. Combined with the outbox payload updates, this keeps the contract clear and ensures misconfigured statuses fail fast via the existingNO_MEMBER_WELCOME_EMAIL/ inactive‑template checks.
cmraible
left a comment
There was a problem hiding this comment.
Couple minor naming notes to self
ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js
Outdated
Show resolved
Hide resolved
ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js
Outdated
Show resolved
Hide resolved
ghost/core/core/server/services/outbox/handlers/member-created.js
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
ghost/core/core/server/services/outbox/handlers/member-created.js (1)
6-8: LGTM – Change aligns with the new payload structure.The handler now correctly reads
statusfrom the payload instead of using a hardcoded value. Based on the learnings and past review discussion, the backwards compatibility concern was addressed by the feature being behind a private labs flag.Optional simplification: You previously suggested removing the separate
memberStatusparameter since the status is now in the payload. If thememberWelcomeEmailService.api.sendmethod can readstatusdirectly from thememberobject, you could simplify to:-await memberWelcomeEmailService.api.send({member: payload, memberStatus: payload.status}); +await memberWelcomeEmailService.api.send({member: payload});This would eliminate the redundancy of passing the same data twice.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js(3 hunks)ghost/core/core/server/services/outbox/handlers/member-created.js(1 hunks)ghost/core/test/integration/jobs/process-outbox.test.js(8 hunks)ghost/core/test/integration/services/member-welcome-emails.test.js(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- ghost/core/test/integration/services/member-welcome-emails.test.js
- ghost/core/test/integration/jobs/process-outbox.test.js
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 25288
File: ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js:46-64
Timestamp: 2025-11-10T23:10:17.470Z
Learning: In ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js and the outbox processing flow, entries are marked as PROCESSING before being processed. If a failure occurs after email send but before deletion, the entry remains stuck in PROCESSING state (not reprocessed). This intentional design prevents duplicate emails. Handling stuck PROCESSING entries is planned for a separate PR.
📚 Learning: 2025-11-10T23:10:17.470Z
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 25288
File: ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js:46-64
Timestamp: 2025-11-10T23:10:17.470Z
Learning: In ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js and the outbox processing flow, entries are marked as PROCESSING before being processed. If a failure occurs after email send but before deletion, the entry remains stuck in PROCESSING state (not reprocessed). This intentional design prevents duplicate emails. Handling stuck PROCESSING entries is planned for a separate PR.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.jsghost/core/core/server/services/members/members-api/repositories/MemberRepository.js
📚 Learning: 2025-10-30T17:13:26.190Z
Learnt from: sam-lord
Repo: TryGhost/Ghost PR: 25303
File: ghost/core/core/server/services/email-service/BatchSendingService.js:19-19
Timestamp: 2025-10-30T17:13:26.190Z
Learning: In ghost/core/core/server/services/email-service/BatchSendingService.js and similar files in the Ghost codebase, prefer using `{...options}` spread syntax without explicit guards like `...(options || {})` when spreading potentially undefined objects, as the maintainer prefers cleaner syntax over defensive patterns when the behavior is safe.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-06-10T11:07:10.800Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 23770
File: apps/admin-x-settings/src/components/settings/email/newsletters/NewsletterDetailModalLabs.tsx:435-450
Timestamp: 2025-06-10T11:07:10.800Z
Learning: In Ghost's newsletter customization features, when promoting a feature from alpha to beta status, the feature flag guards are updated to make the feature available under both the `emailCustomization` (beta) and `emailCustomizationAlpha` (alpha) flags. This is done by either removing conditional guards entirely when the component is already behind both flags, or by updating conditionals to check for either flag.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-08-11T19:39:00.428Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 24651
File: ghost/core/test/utils/urlUtils.js:53-57
Timestamp: 2025-08-11T19:39:00.428Z
Learning: In Ghost's test utilities, when fixing specific issues like async behavior, it's preferred to maintain existing error handling patterns (even if suboptimal) to keep PRs focused on their primary objective. Error handling improvements can be addressed in separate, dedicated PRs.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-06-13T11:57:58.226Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 23824
File: ghost/core/core/server/services/email-service/email-templates/partials/styles.hbs:919-926
Timestamp: 2025-06-13T11:57:58.226Z
Learning: In `ghost/core/core/server/services/email-service/email-templates/partials/styles.hbs`, some style blocks (e.g., `.latest-post p` and `.latest-post p a`) still use the legacy colour `#73818c` on purpose; they are later overridden by `emailCustomization` feature rules, as noted by inline TODO comments. These occurrences should not be flagged as inconsistencies.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-02-03T17:14:37.367Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 22101
File: ghost/core/core/server/api/endpoints/utils/serializers/output/utils/post-gating.js:110-117
Timestamp: 2025-02-03T17:14:37.367Z
Learning: The potential issues with gated content blocks (lack of migration path, race conditions during flag transitions, content visibility when disabled) are accepted trade-offs during the early testing phase of the contentVisibility labs feature.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-10-09T15:31:06.587Z
Learnt from: kevinansfield
Repo: TryGhost/Ghost PR: 25118
File: apps/portal/src/actions.js:160-173
Timestamp: 2025-10-09T15:31:06.587Z
Learning: When reviewing PRs that introduce feature-flagged changes (e.g., `labs?.membersSigninOTCAlpha`), avoid suggesting modifications to non-flagged code paths unless they're directly related to the PR's objectives. Keep the scope focused on the feature-flag-specific changes only.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.js
📚 Learning: 2025-10-09T08:10:18.956Z
Learnt from: rob-ghost
Repo: TryGhost/Ghost PR: 25075
File: ghost/admin/app/services/whats-new.js:77-83
Timestamp: 2025-10-09T08:10:18.956Z
Learning: In ghost/admin/app/services/whats-new.js, reusing `this._whatsNewSettings` (initialized once in `init()`) when persisting defaults for new users without `lastSeenDate` is acceptable. The team is aware that this means sequential logins by different admins in the same tab could share the service initialization timestamp, but this tradeoff is not a concern for their use case.
Applied to files:
ghost/core/core/server/services/outbox/handlers/member-created.js
🧬 Code graph analysis (1)
ghost/core/core/server/services/outbox/handlers/member-created.js (3)
ghost/core/core/server/services/outbox/jobs/lib/process-outbox.js (1)
memberWelcomeEmailService(7-7)ghost/core/test/integration/services/member-welcome-emails.test.js (2)
payload(76-76)payload(147-147)ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js (2)
payload(558-558)payload(624-624)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
- GitHub Check: Legacy tests (Node 22.18.0, mysql8)
- GitHub Check: Legacy tests (Node 22.18.0, sqlite3)
- GitHub Check: Acceptance tests (Node 22.18.0, sqlite3)
- GitHub Check: Ghost-CLI tests
- GitHub Check: Acceptance tests (Node 22.18.0, mysql8)
- GitHub Check: Unit tests (Node 22.18.0)
- GitHub Check: Lint
- GitHub Check: Build & Push Docker Image
🔇 Additional comments (2)
ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js (2)
358-376: LGTM – Conditional logic correctly gates free welcome emails.The gating logic ensures the free welcome email is only sent when:
- The free welcome email feature is active
- The member is signing up for free (no
stripeCustomer)This directly addresses the PR objective of preventing free welcome emails from being sent to new paid members. The
status: 'free'field in the payload is correct since this code path is only reached for free signups.
387-389: LGTM – Event dispatch is consistently gated.The
StartOutboxProcessingEventis only dispatched when an outbox entry has been created, maintaining consistency with the gating logic at line 361. This ensures processing is only triggered for free member signups.
5b45a25 to
67dc363
Compare
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (1)
ghost/core/test/integration/services/member-welcome-emails.test.js (1)
219-226: Consistent payload shape for no‑template scenarioThis payload mirrors the inactive‑template test (including
nameandstatus: 'free'), keeping both branches consistent with the live outbox payload.
🧹 Nitpick comments (3)
ghost/core/test/integration/services/member-welcome-emails.test.js (1)
196-203: Manual outbox payload updated for new status fieldIncluding both
nameandstatus: 'free'in the payload mirrors whatMemberRepository.createnow emits, so this inactive‑template path will exercise the same code as real events.If this payload shape changes again, you may want a small helper to build
MemberCreatedEventpayloads for tests, to avoid having to update multiple literals.ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js (1)
673-715: Good coverage for paid‑signup (stripeCustomer) pathThis test accurately exercises the new behavior: when
stripeCustomeris present, noMemberCreatedEventoutbox entry is created for the free welcome email, even though the inbox config and template are active.You could also assert that
repo.upsertCustomer/repo.linkSubscriptionwere called to ensure the paid‑signup flow still runs while the free welcome email is skipped.ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js (1)
347-390: Free welcome email correctly gated on non‑Stripe signupsThe new
isFreeSignup = !stripeCustomerflag and theisFreeWelcomeEmailActive && isFreeSignupchecks around bothOutbox.addandStartOutboxProcessingEventcleanly prevent free welcome emails from being queued for paid (Stripe) signups, while preserving the behavior for true free signups.Instead of hard‑coding
status: 'free'in the payload, consider deriving it from the member, e.g.:status: newMember.get('status')or from
memberStatusData.status. That keeps the payload in sync if the initial status logic ever changes (for example, if additional non‑paid statuses are introduced) and avoids encoding'free'in multiple places.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
ghost/core/core/server/services/member-welcome-emails/service.js(1 hunks)ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js(3 hunks)ghost/core/core/server/services/outbox/handlers/member-created.js(1 hunks)ghost/core/test/integration/jobs/process-outbox.test.js(8 hunks)ghost/core/test/integration/services/member-welcome-emails.test.js(3 hunks)ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
- ghost/core/core/server/services/member-welcome-emails/service.js
- ghost/core/test/integration/jobs/process-outbox.test.js
- ghost/core/core/server/services/outbox/handlers/member-created.js
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 25288
File: ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js:46-64
Timestamp: 2025-11-10T23:10:17.470Z
Learning: In ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js and the outbox processing flow, entries are marked as PROCESSING before being processed. If a failure occurs after email send but before deletion, the entry remains stuck in PROCESSING state (not reprocessed). This intentional design prevents duplicate emails. Handling stuck PROCESSING entries is planned for a separate PR.
📚 Learning: 2025-11-10T23:10:17.470Z
Learnt from: troyciesco
Repo: TryGhost/Ghost PR: 25288
File: ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js:46-64
Timestamp: 2025-11-10T23:10:17.470Z
Learning: In ghost/core/core/server/services/member-welcome-emails/jobs/lib/process-entries.js and the outbox processing flow, entries are marked as PROCESSING before being processed. If a failure occurs after email send but before deletion, the entry remains stuck in PROCESSING state (not reprocessed). This intentional design prevents duplicate emails. Handling stuck PROCESSING entries is planned for a separate PR.
Applied to files:
ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.jsghost/core/test/integration/services/member-welcome-emails.test.jsghost/core/core/server/services/members/members-api/repositories/MemberRepository.js
📚 Learning: 2025-03-13T09:00:20.205Z
Learnt from: mike182uk
Repo: TryGhost/Ghost PR: 22471
File: apps/admin-x-activitypub/src/utils/pending-activity.ts:13-71
Timestamp: 2025-03-13T09:00:20.205Z
Learning: The pending activity utilities in the Ghost ActivityPub module are covered by tests in the file `apps/admin-x-activitypub/test/unit/utils/pending-activity.ts`.
Applied to files:
ghost/core/test/integration/services/member-welcome-emails.test.js
🧬 Code graph analysis (3)
ghost/core/test/unit/server/services/members/members-api/repositories/MemberRepository.test.js (4)
ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js (1)
config(11-11)ghost/core/core/server/services/member-welcome-emails/service.js (1)
config(5-5)ghost/core/core/server/services/members/members-api/members-api.js (1)
MemberRepository(10-10)ghost/core/core/server/models/outbox.js (1)
Outbox(10-19)
ghost/core/test/integration/services/member-welcome-emails.test.js (1)
ghost/core/core/server/services/outbox/jobs/lib/process-entries.js (1)
payload(51-51)
ghost/core/core/server/services/members/members-api/repositories/MemberRepository.js (2)
ghost/core/core/server/services/members/members-api/members-api.js (1)
newMember(282-282)ghost/core/core/server/services/stripe/services/webhook/CheckoutSessionEventService.js (1)
memberData(197-197)
🔇 Additional comments (1)
ghost/core/test/integration/services/member-welcome-emails.test.js (1)
71-82: Status assertion matches new free‑signup contractAsserting
payload.status === 'free'keeps the integration test aligned with the new outbox payload shape and will catch regressions if the free/paid gating changes in future.
closes https://linear.app/ghost/issue/NY-857/free-member-welcome-email-is-sent-to-new-paid-members
Currently if free welcome emails are enabled, and a new member signs up for a paid subscription, they will receive the free welcome email in error. This is because new paid members are initially created in the
freestatus, until the Stripe subscription is fully processed, at which point they transition topaid.This fixes the bug by opting to skip sending the welcome email if the member has a
stripeCustomerattached, which indicates that the member at least intends to become a paid member.